Ciclo de vida de los componentes

Ciclo de vida de los componentes

El ciclo de vida (life cycle) de un componente, representa las etapas por las que un componente pasa durante toda su vida, desde la creación hasta que es destruido. Conocer el ciclo de vida de un componente es muy importante debido a que nos permite saber cómo es que un componente se comporta durante todo su tiempo de vida y nos permite prevenir la gran mayoría de los errores que se provocan en tiempo de ejecución.

Hasta el momento hemos hablado de muchas características de React, como lo son las props, los estados (state), el shadow dom y controles, pero poco hemos hablado acerca de cómo React administra los componentes. Por lo que en esta unidad nos centraremos exclusivamente en esto.

Para comprender el ciclo de vida de los componentes es importante hablar un poco de la historia de React y como este ha venido evolucionando y madurando su ciclo de vida, en este contexto, es importante mencionar que desde su nacimiento, React ha cambiado ligeramente el clico de vida de los componentes, desaconsejando y agregando nuevos métodos, es por ello que me gustaría mostrar primero que nada el ciclo de vida de React hasta la versión 16.2:

Ciclo de vida de los componentes en React.
Ciclo de vida de los componentes en React.

En la imagen anterior puedes ver todos los métodos del ciclo de vida de los componentes que funcionaban hasta la versión 16.2, donde podemos apreciar de color verde los métodos que siguen vigentes hasta el día de hoy y en rojo los métodos que se han desaconsejado su uso (deprecados). Si bien los métodos deprecados siguen funcionando en todas las versiones de React 16.x, están programados para ser removidos en la versión 17, por lo que hay que tener cuidado de utilizarlos.

Hoy en día los métodos deprecados pueden seguir siendo utilizados con su nombre original, sin embargo, siempre nos arrojará warnings en la consola, a menos que renombremos los métodos con el prefijo UNSAFE_, por ejemplo UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps y UNSAFE_componentWillUpdate. Finalmente, se espera que para la versión 17 de React, solo estén disponibles estos métodos con el prefijo UNSAFE_, por lo que mi consejo es, no utilizar nunca más estos métodos en desarrollos nuevos, pues están programados para ser removidos por completo en versiones posteriores.

Vamos a tratar de mencionar para que sirve todos estos métodos, incluso los desaconsejados, con la intención de que si los ves en algún proyecto antiguo sepas que hacen, pero no nos centraremos de lleno en entender como funcionaba todo este ciclo de vida, pues consideramos que es algo que ya va de salida y no vale la pena invertir mucho tiempo en ello.

Una vez que ya hablamos del ciclo de vida antiguo de React, pasaremos a la nueva configuración del ciclo de vida que está disponible a partir de la versión 16.3:

Ciclo de vida de los componentes en React.
Ciclo de vida de los componentes en React.

En la imagen anterior podemos ver la nueva configuración del ciclo de vida, marcando en amarillo los nuevos métodos que son agregados a partir de la versión 16.3.

Hemos tratado de ordenar los métodos del ciclo de vida según el orden en el que se ejecutan, sin embargo, no siempre se ejecutarán todos los métodos o incluso, habrá otros que se ejecuten más de una vez, por lo que la imagen anterior representa el flujo de un componente que se monta, actualiza (una sola vez) y finalmente de desmonta.

En las siguientes secciones explicaremos con detalle para que sirve cada método de forma individual y seguido explicaremos como interviene cada método en los 3 escenarios posibles por los que puedes pasar un componente, es decir, Montaje, Actualización y Desmontaje.

Para comprender el ciclo de vida de los componentes es super importante entender 3 conceptos fundamentes:

tip

Nuevo concepto: Montaje

En React, el montaje es el proceso por medio del cual el componente es construido y renderizado en pantalla por primera vez, por lo tanto, se considera montado solo cuando el componente ya es visible en pantalla y ya es parte del Document Object Model (DOM).

tip

Nuevo concepto: Actualización

En React, la actualización es el proceso por medio del cual un componte ya montado es actualizado, ya sea por cambiar el state o las props.

tip

Nuevo concepto: Desmontaje

En React, el desmontaje es el proceso por medio del cual un componte es destruido y finalmente removido del Document Object Model (DOM), lo que implica que no sea visible en pantalla.

Dicho lo anterior, podemos ver que métodos del ciclo de vida se ejecutan en cada etapa:

Ciclo de vida de los componentes en React.
Ciclo de vida de los componentes en React.

El Constructor

Antes que nada, me gustaría aclarar que el constructor no es como tal un método del ciclo de vida, sin embargo, lo quise agregar debido a que a partir de la versión 16.3 toma un mayor protagonismo al ser deprecado el método componentWillMount.

Como vamos a ver en el siguiente punto, el método componentWillMount se ejecutaba justo antes de montar el componente, por lo que se utiliza para inicializar el componente, como llamar algún servicio o inicializar el status, sin embargo, a partir de la versión 16.3 de React el método componentWillMount ha sido deprecado, por lo que se aconseja utilizar el constructor para inicializar el componente.

Si bien no es necesario que todos los componentes definan el constructor, es importante hacer algunas aclaraciones para evitar posibles errores en tiempo de ejecución, así que lo analizaremos con un ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React from 'react'  
import { render } from 'react-dom'  
import "core-js/stable";  
import "regenerator-runtime/runtime";  
    
class App extends React.Component {  
    constructor(args){  
        super(args)  
        APIInvoke.invokeGET("/tweets", response => {  
            console.log("api response =>")  
            this.setState(response.body)  
        }, error => {  
    
        })  
    }  
    
    
    render() {  
        console.log("render =>")  
        return (  
            <h1>Hello World</h1>  
        )  
    }  
}  
render(<App />, document.getElementById('root'));    

Lo primero que tenemos que tomar en cuenta es que el constructor recibe como parámetro las propiedades, por lo que es indispensable agregar dicho parámetro. Por otra parte, es super importante que la primera línea del cuerpo del constructor mande llamar al constructor de la super clase mediante super(args), de lo contrario, el componente no será inicializado correctamente y las props no estarán disponibles en tiempo de ejecución.

tip

Error común

No inicializar el constructor de la super clase con super(args) puede provocar que las props no estén disponibles en tiempo de ejecución.

Los siguiente a tomar en cuenta es que cualquier llamada a un servicio externo o cualquier operación que sea asíncrona, dará como resultado que el componente sea renderizado antes de obtener los resultados de esa llamada, por ejemplo, en la clase anterior, hacemos una llamada al API REST mediante la clase APIInvoke, y luego en el método render imprimimos el mensaje “render” en pantalla.

Un programador inexperto esperaría que el método render sea llamado justo después de que el API response, sin embargo, al ser una llamada asíncrona, el constructor termina y una vez terminado, se ejecuta la llamada al API, lo que da como resultado que veamos en el log del navegador la siguiente salida:

1
2
1.	render =>
2.	API response =>  

Esto es importante porque hay que ser precavidos en el método render y no esperar que los valores del estado estén inicializados previo al primer renderizado del componente.

tip

Importante

El constructor solo se ejecuta una sola vez durante todo el ciclo de vida de un componente.

Function getDerivedStateFromProps

La función getDerivedStateFromProps se utiliza en casos muy raros donde el estado del componte depende de las props, de tal forma que este método permite actualizar el estado en función de las propiedades y retornar un nuevo estado.

Este método recibe como parámetro el estado y las propiedades, y deberá retornar un objeto, el cual será establecido como el nuevo estado del componente, o retornar null para indicar que se conserva el estado actual sin aplicarle ningún cambio.

1
2
3
4
5
6
7
8
9
static getDerivedStateFromProps(props, state){  
    if (props.client.id != state.cliente.id) {  
        return {  
            ...state,  
            client: props.client  
        }  
    }  
    return null  
} 

Observa que este es el único método del ciclo de vida que es estático, lo que quiere decir que no tiene acceso a la instancia del componente, por lo tanto, todo lo relacionado al DOM no estará disponible.

tip

Importante

La función getDerivedStateFromProps se ejecuta cada vez que las propiedades del componente cambien y se ejecuta justo después del constructor y justo antes del render, tanto en el montaje como las actualizaciones

tip

Function componentWillMount (deprecated)

La función componentWillMount() se ejecuta antes de que el componente sea montado y antes de la función render(). Se utiliza por lo general para realizar la carga de datos desde el servidor o para realizar una inicialización síncrona del estado.

Esta función no recibe ningún parámetro y se ve de la siguiente manera:

1
2
3
UNSAFE_componentWillMount(){  
    //Any action  
} 

En este punto, los elementos del componente no podrán ser accedidos por medio del DOM, pues aún no han sido creados, esto quiere decir que si buscamos un elemento en el DOM mediante document.getElementById(“id”), este no existirá porque en este punto no ha sido enviado al DOM.

tip

Importante

Recordemos que este método fue deprecado a partir de React 16.3, por lo que no deberíamos utilizarlo más, sin embargo, si se llegara a utilizar, deberá de llevar el prefijo UNSAFE_.

tip

Importante

La función componentWillMount solo se ejecuta una sola vez durante todo el ciclo de vida de un componente.

Function render

El método render() es el único de todos los métodos del ciclo de vida que es obligatorio, pues es el que se encarga de definir como el componente deberá ser renderizado en pantalla, por lo que como resultado deberá retornar cualquiera de los siguientes valores:

  • Elementos de React: Son elementos creados normalmente con ayuda de JSX, como por ejemplo un
    o un componente <MyComponent />.
  • Arrays y fragmentos: Los fragmentos () ya los discutimos con anterioridad, los cuales nos permiten retornar múltiples elementos si la necesidad de retornar un elemento root concreto, por otro lado, es posible retornar un array con múltiples elementos.
  • PortalesLos portales proporcionan una opción de primera clase para renderizar hijos en un nodo DOM que existe por fuera de la jerarquía del DOM del componente padre. Puedes ver más de los portales en la documentación oficial.
  • String y númerosEstos son renderizados como nodos de texto en el DOM.
  • Booleanos o nulosNo renderizan nada, pero es utilizado como estrategia para impedir que el componente se muestre si alguna condición no se cumple.

La función tiene el siguiente formato:

1
2
3
4
5
6
render(){  
    // Vars and logic sección  
    return (  
    //JSX section  
    )  
} 

Podemos dividir el método en dos partes, el cuerpo del método y el retorno, la primera parte (línea 2), nos permite declarar variables, hacer validaciones, llamadas a métodos, o cualquier instrucción JavaScript válida, en segundo lugar tenemos el return (línea 4), donde podemos retornar cualquier de los posibles valores que ya explicamos.

La función render debe ser pura, lo que significa que no deberá modifica el estado del componente, devuelve el mismo resultado cada vez que se invoca con los mismos parámetros y no interactúa directamente con el navegador.

tip

Error común

Actualizar el estado desde la función render puede provocar que el componente se cicle, en un renderizado infinito, ya que cada actualización del estado dispara nuevamente el método render.

tip

Error común

Se debo de evitarse intentar interactuar con el DOM desde el método render, ya que en este punto los elementos no han sido renderizados en pantalla, por lo tanto, no existen en el navegador.

Finalmente, la función render no será invocado si shouldComponentUpdate devuelve falso (hablaremos de esta función más adelante).

tip

Importante

La función render se ejecutará cada vez que el state o las props del componente cambien, lo que significa que se puede ejecutar muchas veces durante todo su ciclo de vida.

Function getSnapshotBeforeUpdate

Según la documentación oficial de React, la función getSnapshotBeforeUpdate se invoca justo antes de que la salida renderizada más reciente se entregue. Permite al componente capturar cierta información del DOM (por ejemplo, la posición del scroll) antes de que se cambie potencialmente. Cualquier valor que se devuelva en este ciclo de vida se pasará como parámetro al método componentDidUpdate.

Dicho de otra forma, este método nos permite recuperar cierta información del componente justo antes de que se actualice en pantalla, de esta forma, podemos mandar lo que encontramos como parámetro al método componentDidUpdate para hacer alguna acción.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
getSnapshotBeforeUpdate(prevProps, prevState) {  
    if (prevProps.list.length < this.props.list.length) {  
        const list = this.listRef.current;  
        return list.scrollHeight - list.scrollTop;  
    }  
    return null;  
    }  
    
    componentDidUpdate(prevProps, prevState, snapshot) {  
    if (snapshot !== null) {  
        const list = this.listRef.current;  
        list.scrollTop = list.scrollHeight - snapshot;  
    }  
} 

Observa que la función getSnapshotBeforeUpdate retorna un valor, el cual es pasado como parámetro a componentDidUpdate (línea 9).

Este es quizás el método más complicado de entender y quizás el menos utilizado de todos, por lo que es probable que no lo utilices en un largo tiempo, pero vale la pena que al menos comprendas el concepto.

tip

Importante

La función getSnapshotBeforeUpdate se ejecutará justo después del método render pero antes de que los cambios sean reflejados en el navegador.

Function componentDidMount

La función componentDidMount() se ejecuta justo después del método render(), cuando el componente ya ha sido montado y todos los elementos del DOM ya están disponibles, y es utilizado para cargar datos del servidor o para realizar operaciones que requieren elementos del DOM. En este punto todos los elementos ya existen en el DOM y pueden ser accedidos.

1
2
3
componentDidMount(){  
    //Any action  
} 

En esta función es seguro modificar el estado o cargar datos del servidor.

tip

Importante

La función componentDidMount solo se ejecuta una sola vez durante todo el ciclo de vida de un componente.

Function componentWillReceiveProps (deprecated)

La función componentWillReceiveProps(nextProps) se invoca cada vez que un componente ya montado, recibe nuevas propiedades. Este método no se ejecuta durante el montaje inicial.

Recibe la variable nextProps que contiene los valores de las nuevas propiedades, por lo que es posible comparar los nuevos valores (nextProps) contra los props actuales (this.props) para determinar si tenemos que realizar alguna acción para actualizar el componente.

1
2
3
UNSAFE_componentWillReceiveProps(nextProps){  
    // Any action  
} 

Hay que tener en cuenta que esta función se puede llamar incluso si no ha habido ningún cambio en las props, por ejemplo, cuando el componente padre es actualizado.

Este método es utilizado con frecuencia cuando el estado del componente depende de las propiedades, lo que nos permite actualizar el estado cuando las propiedades del componente cambien.

tip

Importante

Recordemos que este método fue deprecado a partir de React 16.3, por lo que no deberíamos utilizarlo más, sin embargo, si se llegara a utilizar, deberá de llevar el prefijo UNSAFE_.

tip

Importante

La función componentWillReceiveProps se ejecuta cada vez que las propiedades de un componente ya montado cambien, lo que significa que se puede ejecutar varias veces durante todo su ciclo de vida.

Function shouldComponentUpdate

La función shouldComponentUpdate(nextProps, nextState) se ejecuta justo antes de la función render() y se utiliza para determinar si un componente requiere ser actualizado. Por default, este método siempre retorna true, lo que significa que el método render se ejecutará siempre que el state o las props cambien.

Esta función existe únicamente para mejorar el performance de un componente, permitiéndole determinar si el método render debe de ser ejecutado o no, incluso si el state o las props cambien.

Esta función recibe las nuevas propiedades (nextProps) y el nuevo estado (nextState) y deberá de retornar forzosamente un valor booleano, donde un true le indica a React que el método render deberá ser ejecutado, en otro caso, el método render será omitido y el componente no se actualizará.

1
2
3
4
shouldComponentUpdate(nextProps, nextState) {  
    // Any action  
    return boolean  
} 

tip

Error común

Cabe mencionar que según la misma documentación oficial de React, este método es solo una recomendación, por lo que devolver false, puede incluso dar como resultado una nueva renderización del componente.

tip

Importante

La función shouldComponentUpdate se ejecutará cada vez que el state o las props del componente cambien, por lo que es posible que se ejecute varias veces durante todo el ciclo de vida del componente.

Function forceUpdate

La función forceUpdate no en realidad un método del ciclo de vida de un componente, más bien es un método que tiene todos los componentes para forzar la actualización del componente, brincándose el método shouldComponentUpdate y pasando directo al método render.

1
2
3
4
someMethod() {  
    // Forzar la actualización  
    this.forceUpdate();  
}

Este método puede ser llamado desde cualquier parte del componente, provocando que el método render se ejecuta, incluso si las propiedades y el estado no cambiaron.

Este es un método de emergencia, cuando por alguna razón no logramos que el componte se actualice cuando queremos, por lo que lo debemos de utilizar con cuidado, y en su lugar debemos de procurar ajustarnos al ciclo de vida normal de React.

tip

Importante

La función forceUpdate jamás se ejecuta como parte del ciclo de vida de un componente en React, sino más bien, es llamado bajo demanda por el mismo programador en caso extremos.

Function componentWillUpdate (deprecated)

La función componentWillUpdate(nextProps, nextState) se ejecuta antes de la función render() cuando se reciben nuevas propiedades o estado. Se utiliza para preparar el componente antes de la actualización. Este método no se llama durante el montado inicial del componente.

Tenga en cuenta que no se puede llamar a this.setState() desde aquí. En caso de requerir actualizar el estado en respuesta a un cambio en las propiedades, entonces deberá utilizar el método componentWillReceiveProps().

1
2
3
componentWillUpdate(nextProps, nextState) {  
    // Any action  
}  

La función componentWillUpdate() no será invocado si la función shouldComponentUpdate() retorna false.

tip

Importante

Recordemos que este método fue deprecado a partir de React 16.3, por lo que no deberíamos utilizarlo más, sin embargo, si se llegara a utilizar, deberá de llevar el prefijo UNSAFE_.

tip

Importante

La función componentWillUpdate se ejecuta previo siempre que el state o las props del componte cambien, siempre y cuando el método shouldComponentUpdate retorne true, por lo que se puede ejecutar varias veces durante todo el ciclo de vida del componente.

Function componentDidUpdate

La función componentDidUpdate(prevProps, prevState) se ejecuta justo después del método render(), pero solo cuando es actualizado, lo que implica que no será invocado durante la fase de montaje.

Esta función recibe como parámetro el estado anterior, las propiedades anteriores y el snapshot generado por la función getSnapshotBeforeUpdate, y se utiliza para realizar operaciones sobre los elementos del DOM o para consumir recursos de red, imágenes o servicios del API, sin embargo, es necesario validar el nuevo estado y props contra los anteriores, para determinar si es necesario realizar alguna acción.

1
2
3
4
5
6
componentDidUpdate(prevProps, prevState, snapshot ){    
// Any action    
    if (this.props.userID !== prevProps.userID) {  
    APIInvoke.invokeGET()  
    }  
} 

Este servicio se utiliza a menudo para hacer referencia a elementos del DOM una vez que ya están disponibles, por otra parte, también es utilizado para consultar recursos en la red, como es el caso del API, sin embargo, es importante siempre validar el state y las props para asegurarse de que realmente algo cambio para justificar la búsqueda en la red.

tip

Importante

La función componentDidUpdate se ejecuta siempre que el estado o las propiedades sean actualizadas, con la condición de que el método shouldComponenteUpdate retorne true. Esto quiere decir que este método se puede ejecutar varias veces durante todo el ciclo de vida de los componentes.

Function componentWillUnmount

La función componentWillUnmount() se invoca inmediatamente antes de que un componente sea desmontado y destruido. Se utiliza para realizar tareas de limpieza, como invalidar temporizadores, cancelar solicitudes de red o limpiar cualquier elemento del DOM. Esta función no recibe ningún parámetro.

1
2
3
componentWillUnmount() {  
    // Any action  
}  

Actualizar el estado en este método será en vano, pues el componente nunca más será renderizado.

tip

Importante

La función componentWillUnmount solo se ejecuta una sola vez durante todo el ciclo de vida de un componente.

Flujos de montado de un componente

En esta sección se describe el ciclo de vida por el cual pasa un componente cuando es montado por primera vez. Se dice que un componente es montado cuando es creado inicialmente y mostrado por primera vez en pantalla.

Ciclo de vida del montaje de un componente.
Ciclo de vida del montaje de un componente.

Cuando un componente es montado inicialmente, siempre se ejecutará el constructor de la clase, pues el componente no existe en ese momento. El constructor lo podemos utilizar para inicializar el estado y realizar operaciones de carga de servicios. Cabe mencionar que el constructor no es precisamente parte del ciclo de vida del componente, sino más bien es una característica de los lenguajes orientados a objetos, como es el caso de JavaScript.

Justo antes de que el componente sea renderizado, el método getDerivedStateFromProps es ejecutado, con la intención de actualizar el estado basado en las propiedades. Recordemos que este método regresa el nuevo estado del componte o null para dejar el estado actual.

El siguiente paso es la renderización del componente mediante el método render(). En este momento son creados los elementos en el DOM y el componente es mostrado en pantalla. En este punto no debemos actualizar el estado mediante setState(), pues podemos ciclar en una actualización sin fin.

Una vez que React ha terminado de renderizar el componente mediante render(), ejecuta la función componentDidMount para darle la oportunidad a la aplicación de realizar operaciones sobre los elementos del DOM. En este punto es recomendable realizar carga de datos o simplemente actualizar elementos directamente sobre el DOM.

Flujos de actualización

El flujo de actualización de un componente se dispara cuando se actualiza el estado mediante this.setState(), pero también las propiedades entrantes pueden detonar en la actualización, por lo que en ocasiones la actualización del componente padre, puede detonar en la actualización del estado en los elementos hijos.

Ciclo de vida de la actualización del estado.
Ciclo de vida de la actualización del estado.

Justo antes de que el componente sea actualizado, el método getDerivedStateFromProps es ejecutado, con la intención de actualizar el estado basado en las propiedades. Recordemos que este método regresa el nuevo estado del componte o null para dejar el estado actual.

Cuando React detecta cambios en el estado o las propiedades, lo primero que hará React será validar si el componente debe ser o no actualizado, para esto, existe la función shouldComponentUpdate. Esta función deberá retornar true en caso de que el componente requiera una actualización y false en caso contrario. Si esta función retorna false, el ciclo de vida se detiene y no se ejecuta el resto de los métodos.

El siguiente paso es la actualización del componente en pantalla, mediante la ejecución de la función render(), en este punto los elementos son creados en el DOM.

Entre que el método render termina y los cambios son reflejados en el navegador, el método getSnapshotBeforeUpdate es ejecutado, con la intención de obtener datos relevantes del DOM actual vs el nuevo DOM para pasar estos datos relevantes al método componentDidUpdate como parámetros.

Una vez que el componente es actualizado y los elementos ya son visibles en el navegador, se ejecuta la función componentDidUpdate, el cual permite realizar carga de datos o realizar operaciones que requiera de la existencia de los elementos en el DOM. Recordemos que este método recibe el snapshot de getSnapshotBeforeUpdate.

Flujos de desmontaje de un componente

El ciclo de vida de desmontaje se activa cuando un componente ya no es requerido más y requiere ser eliminado. El desmontaje se pueda dar por ejemplo cuando un componente es eliminado de una lista o un componente es remplazado por otro.

Ciclo de vida del desmontaje de un componente.
Ciclo de vida del desmontaje de un componente.

El ciclo de vida de desmontaje es el más simple, pues solo se ejecuta la función componenteWillUnmount, que es utilizada para realizar alguna limpieza o eliminar algún elemento del DOM antes de que el componente sea destruido.

Aplicaciones reactivas con React, NodeJS & MongoDB

Todo lo que acabas de ver en este artículo es solo una pequeña parte del libro Aplicaciones reactivas con React, NodeJS & MongoDB, El libro más completo en español para aprender a crear aplicaciones web completas con las tecnologías más potentes de la actualidad, desde el Frontend con React, hasta el Backend con un poderoso API REST con NodeJS y Express y persistiendo todo en MongoDB. te invito a que veas mi libro:

Ver libro
Todos los derechos reservados ©