"El buen diseño es obvio. El gran diseño es transparente"
- Joe Sparano

Como se usa el Hook useEffect en React

5 minutos de lectura
Fecha: 18/11/2021

El Hook useEffect es uno de los más útiles y los que más se usan en el desarrollo de una aplicación con React.

Su uso es muy sencillo y no es nada difícil de entender como funciona, de hecho la documentación oficial que aquí os dejo está muy bien. Yo voy a intentar además poner ejemplos y capturas para que sea más fácil de entender.

La propia documentación lo define así

El Hook de efecto te permite llevar a cabo efectos secundarios en componentes funcionales

De esto podemos deducir una cosa, si ejecuta efectos secundarios sobre el componente, este entonces tiene que estar renderizado previamente, así que se ejecutará cuando ya esté montado.

Desde este punto de vista podemos equipararlo a un trigger que se ejecuta en el componentDidMount dentro del ciclo de vida de un componente.

Por otra parte si es necesario ejecutar el efecto cuando el componente este renderizado, también lo será cuando se vuelva a renderizar porque se cambien cosas, y desde ese punto de vista también equivaldría a dispararlo en el componentDidUpdate.

En resumen es como tener una función que llamamos en estos dos estados del ciclo de vida. componentDidMount y componentDidUpdate.

¿Cómo se usa?

Dicho esto vamos a verlo en funcionamiento. Me he creado un proyecto de cero y voy a meterlo en el componente principal.

Descripción de la imagen

Listo como vemos la sintaxis básica para escribir este hook es una función a la cual le pasas como primer parámetro una arrow function, entiendo que sabes lo que son pero si tienes dudas aquí tienes un artículo donde hablo de ellas…

useEffect(() => {
    // LA LÓGICA QUE QUIERAS QUE SE EJECUTE
});

En mi caso la función que le paso simplemente ejecuta un log para que en la consola se lea ‘Se ejecuta el Hook useEffect!‘, veamos que realmente lo pinta…

Descripción de la imagen

Si el componente se vuelve a renderizar, vuelve a entrar por aquí. Para demostrarlo, sabiendo que cada vez que cambiamos una propiedad el componente se renderiza, vamos a forzar que una propiedad cambie cada segundo y vemos lo que pasa.

En mi caso voy a poner este código.

Descripción de la imagen

Lo que hago ahora es modificar una propiedad del componente (en este caso contador) un segundo después de que se ejecute el useEffect.

Al ser el propio Hook el que dispara ese cambio esto va a producir un bucle infinito, no va a tirar la pagina porque el intervalo es de un segundo, pero se ejecutará indefinidamente.

Además dentro del log he puesto dos lineas, la primera dice ‘Se ejecuta el Hook useEffect porque cambio el valor del contador’ y la segunda nos da la hora exacta de su ejecución y el valor del contador. Y sucede esto…

Descripción de la imagen

He puesto dos recuadros para enfatizar que la primera vez entra de manera automática como antes (de manera similar al componentDidMount) y las siguientes suceden porque se cambia una propiedad y se vuelve a renderizar el componente (como en el componentDidUpdate).

¿Cómo se controla?

Por último vamos a ver como controlar este comportamiento, porque no vamos a querer siempre que por cualquier cambio de una propiedad se ejecute el Hook useEffect. Y esto es normal porque si tuviéramos una lógica compleja dentro de él, llamarlo continuamente cuando cambia cualquier propiedad podría originar un problema de rendimiento.

Al principio decíamos que este hook es una función a la cual le pasas como primer parámetro una arrow function, lo que no dijimos es que como segundo parámetro le podemos pasar un array con las propiedades que tiene que vigilar para volver a ejecutarse cuando cambian.

Si dejamos el array en blanco le estamos diciendo al hook que se ejecute cuando cambien cualquier propiedad, pero si le pasamos alguna propiedad solo vigilara esa/s para volver a ejecutarse.

Y es clave saber que podemos hacer esto porque es la manera de controlar el Hook useEffect. De hecho podríamos haberlo declarado de estas dos maneras.

// ESTA MANERA DE ESCRIBIRLO
useEffect(() => {
    // LA LÓGICA QUE QUIERAS QUE SE EJECUTE
});
// ES IGUAL QUE ESTA
useEffect(() => {
    // LA LÓGICA QUE QUIERAS QUE SE EJECUTE
}, []);

//PERO SI ESCRIBIERAS ESTO, SOLO SE VOLVERÍA A EJECUTAR CUANDO CAMBIE ESA PROPIEDAD
useEffect(() => {
// LA LÓGICA QUE QUIERAS QUE SE EJECUTE
}, [nombreDeLaPropiedad]);

Para demostrarlo voy a modificar el código creando un segundo contador para que quede así:

  1. El primer contador ahora solo muestra pares y se sigue ejecutando cada segundo.
  2. El segundo solo muestra impares y se va a ejecutar cada 5 segundos
  3. Y además le voy a decir que solo ‘vigile’ el segundo contador

Descripción de la imagen

Y esto es lo que nos muestra la consola del navegador

Descripción de la imagen

Fijaros sobretodo en que el momento en el que entra dentro de el Hook useEffect, es cada 5 segundos lo que quiere decir que solo esta volviéndose a ejecutar cuando la propiedad contador2 cambia.

Saneamiento

Por último voy a explicar como sanear el Hook useEffect, y cuando hablamos de sanear nos referimos a que ciertos efectos necesitan de ser saneados para que no produzcan memory leaks por andar acumulándose unos encima de otros.

Por ejemplo si nos suscribimos a un endpoint que nos devuelve una serie de datos dentro del hook y empiezan a cambiar propiedades y volverse a ejecutar, lo que nos va a pasar es que después de 100 cambios vamos a tener 100 suscripciones al mismo endpoint.

Lo mismo pasa si ejecutamos un listener dentro, por ejemplo esperando a que pulsemos un botón, después de 100 cambios tenemos 100 listeners, cuando pulsemos el botón la función de callback se ejecutará 100 veces.

Cuando pasan estas cosas la memoria que consume el navegador empieza a subir hasta que todo se ralentiza y hay que cerrar la página. Para evitar los podemos sanear cancelándolos antes de volver a crearlos.

Esto se hace de la siguiente manera, la función que pasamos como primer parámetro puede devolver otra función que se ejecutará antes de que se vuelva a ejecutar el hook. Es decir que lo que metas en esa función equivaldría al ciclo de vida componentWillUnmount.

Sería así

useEffect(() => {
    // LA LÓGICA QUE QUIERAS QUE SE EJECUTE
    return () => {
        // AQUÍ SANEAMOS TODO LO NECESARIO
    };
});

Y hasta aquí todo lo que considero importante saber de el Hook useEffect, aunque la entrada es larga por los ejemplos, son 4 conceptos los que hay que tener claros, espero que os sirva de ayuda.