Supongamos que tengo un componente principal en el que estoy mostrando un componente de gráfico. Este componente de gráfico toma una serie de datos de tiempo y los traza si los datos cumplen con ciertos criterios, si no, pasa un mensaje de error al padre a través de un controlador de estado de mensaje de error pasado por el padre al hijo.
Ahora, si la serie de tiempo no valida la condición, entonces el elemento secundario actualizaría el mensaje de error, lo que provocaría una nueva representación del elemento secundario y nuevamente conduciría a que el elemento secundario ejecute el código y actualice nuevamente el mensaje de error, lo que generará un bucle infinito.
Dos preguntas aquí:
Consulte el pseudocódigo a continuación
const parent = () => { const [error,setError] = useState(null); return ( <div> <Child data={[[1,2],[3,4],[4,5]]} onError={setError}/> <ErrorDisplay error={error}/> </div> ); } const child = ({data, onError}) => { if (!someCondition(data)) onError("There is some error") return ( <Chart data={data}/> );}La razón por la que no experimenta el bucle es por
Salir de una actualización de estado
Si actualiza un State Hook al mismo valor que el estado actual, React se rescatará sin renderizar a los niños ni disparar efectos. (React usa el algoritmo de comparación Object.is).
Como señala Gabriele Petrioli , siempre que nada más cambie, no estará en un ciclo interminable porque una actualización de estado que cambia exactamente al mismo valor no provoca una nueva representación. Pero, de forma predeterminada, aún haría que el niño hiciera el trabajo dos veces (una vez para encontrar el error, luego otra vez cuando el padre vuelve a renderizar para mostrar el error y se llama al niño nuevamente).
Hay al menos un par de formas de evitar esa duplicación:
Mantenga el estado de error en Child , no en Parent , y no vuelva a calcular error si data no se modifican.
Memo -ize the Child para que la función no se vuelva a llamar cuando sus accesorios no hayan cambiado.
Aquí hay una versión del n. ° 2 que se basa en la identidad de la matriz (es decir, no volverá a ejecutar Child si se le proporciona la misma matriz ):
const Child = React.memo(({data, onError}) => { if (!someCondition(data)) { onError("There is some error") return null; // Or whatever } return ( <Chart data={data}/> ); });Una versión más defensiva podría verificar si la nueva matriz es equivalente a la anterior, incluso si no son la misma matriz:
const childPropsAreEqual = (prevProps, currProps) => { // Where `deepEquals` is a function that does a deep equivalence // check on the array return deepEquals(prevProps.data, currProps.data); }; const Child = React.memo(({data, onError}) => { if (!someCondition(data)) { onError("There is some error") return null; // Or whatever } return ( <Chart data={data}/> ); }, childPropsAreEqual); // <== Note providing an "are equal" function