Digamos que tengo algún estado que depende de algún otro estado (por ejemplo, cuando A cambia, quiero que B cambie).
¿Es apropiado crear un gancho que observe A y establezca B dentro del gancho useEffect?
¿Se activarán los efectos en cascada de tal manera que, cuando haga clic en el botón, se active el primer efecto, lo que provocará que b cambie, lo que provocará que se active el segundo efecto, antes del siguiente renderizado? ¿Hay alguna desventaja de rendimiento en la estructuración de un código como este?
let MyComponent = props => { let [a, setA] = useState(1) let [b, setB] = useState(2) useEffect( () => { if (/*some stuff is true*/) { setB(3) } }, [a], ) useEffect( () => { // do some stuff }, [b], ) return ( <button onClick={() => { setA(5) }} > click me </button> ) }En términos generales, usar setState dentro useEffect creará un bucle infinito que muy probablemente no querrás causar. Hay un par de excepciones a esa regla que abordaré más adelante.
useEffect se llama después de cada procesamiento y cuando setState se usa dentro de él, hará que el componente se vuelva a procesar, lo que llamará a useEffect y así sucesivamente.
Uno de los casos populares en los que usar useState dentro de useEffect no causará un bucle infinito es cuando pasa una matriz vacía como segundo argumento a useEffect como useEffect(() => {....}, []) lo que significa que la función de efecto debe llamarse una vez: solo después del primer montaje/renderizado. Esto se usa ampliamente cuando está obteniendo datos en un componente y desea guardar los datos de la solicitud en el estado del componente.
Para propósitos futuros, esto también puede ayudar:
Está bien usar setState en useEffect , solo necesita tener atención como ya se describió para no crear un bucle.
Pero no es el único problema que puede ocurrir. Vea abajo:
Imagine que tiene un componente Comp que recibe props del padre y, de acuerdo con un cambio de props , desea establecer el estado de Comp . Por alguna razón, debe cambiar para cada accesorio en un useEffect diferente:
NO HAGAS ESTO
useEffect(() => { setState({ ...state, a: props.a }); }, [props.a]); useEffect(() => { setState({ ...state, b: props.b }); }, [props.b]);Es posible que nunca cambie el estado de a, como puede ver en este ejemplo: https://codesandbox.io/s/confident-lederberg-dtx7w
La razón por la que esto sucede en este ejemplo es porque ambos useEffects se ejecutan en el mismo ciclo de reacción cuando cambia prop.a y prop.b , por lo que el valor de {...state} cuando setState es exactamente el mismo en ambos useEffect porque están en el mismo contexto. Cuando ejecute el segundo setState , reemplazará al primer setState .
HAZ ESTO EN SU LUGAR
La solución para este problema es básicamente llamar a setState así:
useEffect(() => { setState(state => ({ ...state, a: props.a })); }, [props.a]); useEffect(() => { setState(state => ({ ...state, b: props.b })); }, [props.b]);Consulte la solución aquí: https://codesandbox.io/s/mutable-surf-nynlx
Ahora, siempre recibe el valor más actualizado y correcto del estado cuando continúa con setState .
¡Espero que esto ayude a alguien!
Los efectos siempre se ejecutan después de que se completa la fase de procesamiento, incluso si establece State dentro de un efecto, otro efecto leerá el estado actualizado y tomará medidas sobre él solo después de la fase de procesamiento.
Habiendo dicho eso, probablemente sea mejor tomar ambas acciones con el mismo efecto, a menos que exista la posibilidad de que b pueda cambiar debido a razones distintas a changing a en cuyo caso también querrá ejecutar la misma lógica.