Estoy tratando de entender la nueva API de ganchos de reaccionar. Específicamente, estoy tratando de construir el caso de uso clásico que alguna vez fue el siguiente:
componentDidUpdate(prevProps) { if (prevProps.foo !== this.props.foo) { // animate dom elements here... this.animateSomething(this.ref, this.props.onAnimationComplete); } } Ahora, traté de construir lo mismo con un componente de función y useEffect , pero no puedo encontrar la manera de hacerlo. Esto es lo que probé:
useEffect(() => { animateSomething(ref, props.onAnimationComplete); }, [props.foo]); De esta manera, el efecto solo se llama cuando cambia props.foo. Y eso funciona, ¡PERO! Parece ser un antipatrón ya que eslint-plugin-react-hooks marca esto como un error. Todas las dependencias que se usan dentro del efecto deben declararse en la matriz de dependencias. Eso significa que tendría que hacer lo siguiente:
useEffect(() => { animateSomething(ref, props.onAnimationComplete); }, [props.foo, ref, props.onAnimationComplete]); Eso no conduce al error de pelusa, PERO anula totalmente el propósito de solo llamar al efecto cuando cambia props.foo . No QUIERO que se cante cuando los otros accesorios o el árbitro cambien.
Ahora, leí algo sobre el uso de useCallback para envolver esto. Lo intenté pero no llegué más lejos.
¿Alguien puede ayudar?
Recomendaría escribir esto de la siguiente manera:
const previousFooRef = useRef(props.foo); useEffect(() => { if (previousFooRef.current !== props.foo) { animateSomething(ref, props.onAnimationComplete); previousFooRef.current = props.foo; } }, [props.foo, props.onAnimationComplete]); No puede evitar la complejidad de tener una condición dentro del efecto, porque sin ella ejecutará su animación en el montaje en lugar de solo cuando cambie props.foo . La condición también le permite evitar la animación cuando cambian otras cosas que no sean props.foo .
Al incluir props.onAnimationComplete en la matriz de dependencias, evita deshabilitar la regla de pelusa, lo que ayuda a garantizar que no presente errores futuros relacionados con las dependencias que faltan.
Aquí hay un ejemplo de trabajo:
Suprime el linter porque te da un mal consejo. React requiere que pases al segundo argumento los valores que (y solo los que) cambios deben desencadenar un efecto de disparo.
useEffect(() => { animateSomething(ref, props.onAnimationComplete); }, [props.foo]); // eslint-disable-line react-hooks/exhaustive-depsConduce al mismo resultado que la solución de Ryan .
No veo ningún problema en violar esta regla de linter. A diferencia de useCallback y useMemo , no generará errores en el caso común. El contenido del segundo argumento es una lógica de alto nivel.
Incluso puede querer llamar a un efecto cuando cambia un valor extraño:
useEffect(() => { alert(`Hi ${props.name}, your score is changed`); }, [props.score]);Mueva los valores, que deben ser nuevos (no obsoletos) en la devolución de llamada pero no deben volver a activar el efecto, a refs:
const elementRef = useRef(); // Ex `ref` from the question const animationCompleteRef = useRef(); animationCompleteRef.current = props.onAnimationComplete; useEffect(() => { animateSomething(elementRef, animationCompleteRef.current); }, [props.foo, elementRef, animationCompleteRef]); Funciona porque el valor de retorno useRef no cambia en los renderizados.