React useEffect
es quizá el hook que más confusiones genera a la hora de utilizarlo.
Algunas de esas confusiones se debe al intento de comparar su funcionamiento con los estados del ciclo de vida de un componente de clase, algo que aclaro en este post anterior
Recomendado:
El hook useEffect recibe dos argumentos, una funcion/callback que define el efecto deseado y un listado/arreglo de valores que definen las dependencias del efecto.
Estas dependencias le sirven a React para saber cuándo o más bien por qué el efecto debe ejecutarse.
Internamente useEffect "observa" este listado de dependencias y cuando uno de los valores de ellas cambia el efecto es emitido. Esto te permite optimizar la ejecución del efecto.
Mantén en mente que React realiza una comparación utilizando Object.is para determinar si hubo un cambio en uno de los elementos. Si necesitas hacer una comparación "profunda" puedes utilizar este hook useDeepCompareEffect
El equipo de React provee un plugin de eslint que ayuda a identificar cuando hay dependencias no identificadas por medio de la regla: react-hooks/exhaustive-deps
En general un efecto es una función que ejecuta cierta lógica para sincronizar el estado interno del componente con un estado externo (si, no me canso de repetirlo 🤷♂️).
useEffect(() => {
fetch("/api/data").then(
res => setState(res.data)
)
}, [setState])
Y por que las dependencias son importantes?
Simple! Por que las dependencias son la forma de controlar cuando el efecto se ejecuta o no. Recuerda. No se trata de si el efecto ocurre al montar el componente (o cualquier otro "momento"), si no, de por qué se ejecuta (cambio en un valor de una dependencia?
Y ¿por qué debo escribir todas las dependencias del efecto?
Si tu efecto utiliza un valor dentro de su lógica pero no lo declaras como dependencia entonces "algo huele mal"(code smell)
El plugin de eslint reportará la dependencia faltante como un warning. Entonces ¿Por qué es tan importante si sólo se reporta como un warning?.
Bueno, es un bug que en cualquier momento volverá a morderte.
Tu efecto funciona incluso sin declarar la dependencia por que la función/callback definida funciona como un closure y es capaz de utilizar el valor externo a su scope.
Si miramos el mismo ejemplo anterior, podemos escribirlo sin dependencias o con una lista vacía (lo que indica que se ejecutará sólo una vez)
useEffect(() => {
fetch("/api/data").then(
res => setState(res.data)
)
}, [])
Pero, piénsalo así, estas declarando una función que trabaja con ciertos valores pero no le estas dando acceso directo a esos valores!! No es extraño?
¿Estás diciendo que tu función usa un valor pero que no depende de él?
¿Entonces que hacer?
Hay que redefinir la lógica de tu efecto para que solo utilice los valores de los que realmente dependa.
¿Cómo? Una forma es extraer la lógica hacia una función externa y utilizar esta nueva función como dependencia del efecto.
Te dejo algunos muy buenos artículos sobre useEffect:
Kent C Dodds: Escrbió [un articulo]((https://kentcdodds.com/blog/react-hooks-pitfalls) sobre algunos errores al utilizar hooks, en donde comenta sobre el no uso de las dependencias.
Dan Abramov tiene un artículo en profundidad sobre useEffect
Y este ártículo de Dave Ceddia
Si te gustó este post considera unirte al newsletter MicroBytes para recibir microcursos sobre desarrollo web.
También puedes ver más contenido en @eggheadio
Top comments (8)
Entonces, es buena practica listar en nuestro arreglo de dependencias todas las variables y funciones que esten siendo usadas en el callback de useEffect? Saludos
Exacto!.. hacerle caso al plugin de eslint.
Es posible hacer una petición fetch sin pasarle una dependencia? Eso sería buena práctica o no?
Si no agregas las dependencias (y si realmente no tiene dependencias) entonces ese efecto se ejecutará solo una vez.
Por lo general este caso de uso es para obtener datos y después guardarlos en algún estado, por lo tanto, si tendrías una dependencia. La función para definir ese estado proviniente de useState que es ene efecto idempotente y no iniciada un nuevo render.
entonces en tu ejemplo sería recomendable mover la función fetch fuera del useEffect? asumiendo que en la mayoria de los casos este fetch podría venir acompañado de más cosas como un dispatch .
El problema de mover
fetch
fuera del efecto es que ahora esa función que crearás será dependencia del efecto, y si no es idempotente entonces será creada en cada re-render por lo que el efecto se emitirá constantemente.Puedes usar React.useCallback para evitar esto.
Mi pregunta es más que nada porque al meter todo dentro del useEffect me sugiere agregar el dispatch en el array de dependencias y se siente extraño, es normal que así sea?
Bueno dispatch es una función "especial". React te asegura que no cambiará entre renders (es idempotente) por lo que, en este caso particular, no afecta su presencia (o ausencia) en las dependencias