Introducción
En el post ReactJS: Conceptos Básicos, desarrollamos el concepto del virtualDOM y la actualización de la vista generada producto de la modificación de un valor del Objeto State de un Componente. Dicha actualización se produce en un proceso de cuatro fases conocido como Ciclo de Vida. React nos permite realizar acciones adicionales durante la ejecución de cualquiera de esas cuatro fases.
Fases del Ciclo de Vida
- Inicialización - Se ejecuta por única vez la primera vez que cargamos la vista. Esta es la fase en la que el virtualDOM crea los valores iniciales de State y sus correspondientes escuchas para posterior actualización
- Montaje - Se ejecuta inmediatamente después de la Fase de Inicialización, si es la primera vez que cargamos la vista; o después de la Fase de Actualización, si se modificó alguno de los valores creados en la Inicialización. Esta es la Fase que permite que el Componente creado en el virtualDOM se renderice en el DOM real.
- Actualización - Se ejecuta como consecuencia del cambio de valor de State de Componente montado (renderizado) en el DOM real. El virtualDOM actualiza los valores creados durante la Inicialización, ejecuta la Fase de Desmontaje para remover el Componente con los valores viejos del DOM real y, por último, ejecuta la Fase de Montaje para renderizar el Componente con los valores de State actualizados.
- Desmontaje - Se ejecuta cada vez que se necesita eliminar un Componente renderizado en el DOM real.
Métodos del Ciclo de Vida
Ahora bien, al momento de crear las UI es muy probable que necesitemos realizar acciones en algún punto de este Ciclo de Vida, para eso React nos ofrece los Métodos del Ciclo de Vida. Estos métodos son funciones que podemos utilizar para agregar lógica personalizada al Ciclo de Vida.
El Ciclo de Vida está atado al cambio de un valor en el Objeto State por lo que, los únicos Componentes que pueden manipular estos métodos son los Componentes de Clase. Por su puesto, al igual que lo que sucede con el Hook useState, a partir de la versión 16.8 existe el Hook useEffect que nos permite la manipulación de estos métodos en un Componente Funcional.
Inicialización
En la Fase de Inicialización nos encontramos con los métodos constructor y render.
El método constructor es usado por las clases para indicar cuáles son los valores iniciales de una instancia de esa clase; mientras que el método render es usado en React para indicar qué vista se debe mostrar cuando el componente se monte en el DOM real.
Todo Componente ejecutará su método constructor la primera vez que se genere la vista. Una vez finalizado, con los valores de State correctamente almacenados en el virtualDOM se ejecuta el método render.
Montaje
En la Fase de Montaje tenemos el método componentDidMount y render.
El método componentDidMount se ejecuta inmediatamente después de la ejecución del método render correspondiente a la Fase de Inicialización. En este punto, el Componente ya se encuentra en DOM real, por lo que es posible utilizar las API Web del navegador. Por ejemplo, si queremos hacer una petición AJAX, debemos emplear este método porque el virtualDOM no tiene la capacidad de realizar peticiones AJAX.
Algo a tener en cuenta del método componentDidMount es que si se cambia algún valor de State producto de su ejecución, de forma automática desata la Fase de Actualización del Ciclo de Vida, lo que, en consecuencia, genera un nuevo renderizado del Componente. Al usar este método hay que tener cuidado de no incurrir en un loop infinito de renderizado de Componente.
Actualización
En la Fase de Actualización encontramos el método componentDidUpdate y render.
Este método no suele trabajarse mucho porque implica generar una acción inmediatamente después de que se haya ejecutado un cambio que desata el Ciclo de Vida. El ejemplo más expandido de uso para este método es la concatenación de peticiones AJAX.
Desmontaje
Por último, en la Fase de Desmontaje contamos con el método componentWillUnmount.
El final del Ciclo de Vida implica la destrucción del Componente. Ahora bien, existen ciertos elementos que quedan almacenados en la memoria de navegador sin importar que el Componente ya no exista. Para no saturar la memoria es importante también eliminar esos elementos. Acá es donde es útil el método componentWillUnmount porque se ejecuta inmediatamente antes de la destrucción del Componente.
Hook useEffect
Una vez que entendimos qué son y cómo funcionan los métodos del Ciclo de Vida, vamos a analizar el uso del Hook useEffect para aprovechar sus características en los Componentes Funcionales.
Recordemos que los Hooks son funciones agregadas a partir de la versión 16.8 de React para permitir que las funcionalidades dependientes del Objeto State (mismo que solo puede existir en los Componentes de Clase) puedan ser aplicadas a los Componentes Funcionales.
En otros posts analizamos el Hook useState que es el encargado de la emulación del trabajo de los valores de State en un Componente. Es decir, gracias al Hook useState, el virtualDOM puede utilizar valores almacenados en los Componentes Funcionales para desatar el Ciclo de Vida.
Como ya sabemos, el Ciclo de Vida de un Componente implica el montaje inicial con el correspondiente almacenamiento de valores y la actualización de ese montaje cuando alguno de esos valores almacenados es modificado durante la interacción con el usuario. También vimos que es posible aplicar los Métodos del Ciclo de Vida para realizar acciones en algún punto del proceso de actualización. El Hook useEffect, entonces, es el encargado de emular el trabajo de los Métodos del Ciclo de Vida en un Componente Funcional.
Consideraciones a tener en cuenta:
- El Hook useEffect es una única función desde la cual podemos manejar la totalidad de los Métodos del Ciclo de Vida
- Es posible usar más de un Hook useEffect en el mismo Componente
Composición del Hook useEffect:
- Función Callback para indicar qué hacer cuando se desate el efecto
- Array de dependencias para manejar cuándo se desata el efecto.
- [] - El efecto se desata solo cuando el Componente es montado por primera vez
- [valorState] - El efecto se desata cuando el componente es montado y cuando cambia el valor almacenado en valorState
- No poner el Array - El efecto se desata cuando es montado y siempre que cambie cualquier valor almacenado en el State (no se recomienda)
- return maneja qué hacer cuando se desmonte el Componente. No es obligatorio su uso
Consideraciones de uso:
- Si la dependencia de nuestro useEffect es un objeto, el efecto se ejecutará siempre porque un objeto sólo puede ser igual a sí mismo. Si vamos a usar un objeto para un valor de state que maneja un efecto debemos agregar las dependencias de forma individual
- Al usar intervalos, agregar escuchas de eventos o realizar cualquier acción que implica almacenar elementos en la memoria del navegador debemos asegurarnos de eleiminarla al momento de desmontar el componente usando el return del Hook useEffect
Ciclo de Vida y Componentes Hijo
Cuando estamos manipulando el Ciclo de Vida de los Componentes es importante tener muy en claro el Árbol de Componentes.
Trabajar con el Renderizado Condicional implica que el virtualDOM analiza los valores del State para saber qué renderizar. Es decir, si X valor es true, entonces mostramos X vista. Si a esta idea le agregamos Eventos, entonces podemos armar, de forma muy sencilla, una vista dinámica para mostrar u ocultar elementos según la interacción del usuario con nuestra App.
Ahora bien, ¿por qué es relevante este concepto?. Sencillo, el Renderizado Condicional depende del Ciclo de Vida del Componente y todos los Componentes que se encuentren dentro del Componente que está siendo afectado por el Renderizado Condicional van a ver desatado su Ciclo de Vida también.
Vamos a imaginar que un Componente, llamémosle Componente A, es una caja. Dentro de esa caja ponemos otros Componentes (otras cajas). Los Componentes B y C.
Si movemos el Componente A (la caja que contiene a los Componentes B y C), vamos a mover los Componentes que contiene (B Y C).
Si rompemos el Componente A, vamos a romper los Componentes B y C.
Si desatamos el Ciclo de Vida del Componente A, se desatan los Ciclos de Vida de los Componentes B y C.
Una vez entendido esto, pensemos lo siguiente. Creamos un Componente A para que sea renderizado de forma condicional cuando el usuario haga clic en un botón. Dentro de ese Componente, creamos en Componente B en el que usados el Hook useEffect con el array de dependencias vacío para realizar una acción cuando el Componente B se monte. El Componente B se va a montar y desmontar cada vez que el Componente A se monte o desmonte producto de la interacción del usuario con la App, por lo tanto, la lógica planteada dentro del useEffect se ejecutará cada vez que el usuario muestre u oculte al Componente A.
En este ejemplo, de forma muy básica, pudimos ver cómo se desata el efecto del Componente B aunque la intención era montar el Componente A. Es por ese motivo que cuando manipulamos el Ciclo de Vida de un Componente debemos prestar especial atención al Árbol de Componentes para evitar ejecuciones no deseadas.
Conclusión
Cuando nuestra aplicación se cargue por primera vez, el virtualDOM almacena los datos guardados en todos los valores de State de los distintos Componentes e inserta los nodos creados a partir del código JSX en el DOM real.
Cuando el usuario realiza una acción que modifica alguno de esos valores iniciales, el virtualDOM elimina ese Componente (y todos los Componentes que contiene) del DOM real. Actualiza los valores correspondientes y vuelve a insertarlo con la vista actualizada.
Todo el intercambio entre el virtualDOM y el DOM real sucede sin necesidad de que el desarrollador intervenga, pero, de ser necesario, es posible efectuar alguna acción durante la misma utilizando los Métodos del Ciclo de Vida (Hook useEffect).
El Renderizado Condicional de un Componente que contenga Componentes que ejecuten acciones durante el Ciclo de Vida, desata dichas acciones.
Redes