Ciclo de vida de los componentes React

En este post voy a dar un repaso a fondo al ciclo de vida de los componentes React con el fin de entender un poco mejor como funcionan y sobre todo en qué casos podemos usar cada uno de los métodos que intervienen en su ciclo de vida.

2015 ha sido sin duda el año de React, y he de decir que no me extraña, desde que he tenido la suerte de trabajar con esta herramienta, cada día me gusta más. Sin ser el precursor de este enfoque hacia la modularización de la web en sistemas basados en componentes, los “chavales” de Facebook han sabido sacarle jugo y personalmente opino que para los desarrolladores front-end (como yo), este enfoque resulta muy interesante y práctico ya que entre otras cosas hace que nuestros proyectos sean muy fácilmente extensibles y modulares.

 

ciclo-vida-componente-react

 

Hasta donde entiendo, un componente React puede ser una función o una clase que extiende de React.Component. En cualquier caso, recibirá propiedades y devolverá un elemento o árbol de elementos que serán renderizádos en nuestro navegador en forma de nodos del DOM por medio de método método render(). Este método es el único requerido a la hora de crear un componente (y el constructor() si picas código en ES6).

La estructura más básica de un componente sería algo así.

class MyComponent extends React.Component {
  // Constructor method
  constructor (props) {
    super(props)
  }

  // Render method
  render () {
    return(
      <div>
        <h1 className="h1-class">Hi! I'm a React component!</h1>
      </div>);
  }
}


export default MyComponent;

Ok, este componente generaría un marcado HTML similar a este.

<div data-reactid=".0.2">
  <h1 class="h1-class">Hi! I'm a react component!</h1>
</div>

Hasta aquí poco más que contar, la cosa está bastante clara. Es el momento de entender de que va eso de el “ciclo de vida” de un componente.

Al lío!

 

Ciclo de vida

Para comprender el comportamiento de los componentes de React es importante comprender su ciclo de vida y los métodos que intervienen en dicho ciclo. Lo interesante de esto es que estos métodos se pueden sobreescribir para conseguir que el componente se comporte de la manera que se espera.

Podemos plantear cuatro escenarios diferentes que pueden darse en el ciclo de vida de un componente.

  • Montaje o inicialización
  • Actualización de estado
  • Actualización de propiedades
  • Desmontaje

Veamos con más detalle cada uno de estos escenarios.

 

Montaje de un componente

El primero de los escenarios que se puede dar en el ciclo de vida de un componente es su montaje. Este escenario se da la primera vez que se crea el componente y los métodos que intervienen son los siguientes.

 

montaje-de-un-componente-react

 

getDefaultProps

Este método se invoca una vez se crea la clase y antes de que se renderize el componente. Su valor se pasa al componente mediante el objeto this.props y este valor será compartido por todas las instancias del componente.

En ES6 no contamos con este método pero por suerte podemos definir propiedades por defecto declarando un objeto estático defaultProps o bien por medio de la propia clase.

static defaultProps = { initialCount: 0 };
// or
MyComponent.defaultProps = { initialCount: 0 };

 

getInitialState

Análogo a getDefaultProps, este método se invoca una sola vez antes de que el componente se haya montado y el valor de retorno se utiliza como valor inicial para this.state.

Al igual que ocurre con las propiedades por defecto, en ES6 tampoco contamos con este método pero  podemos definir estados por defecto declarando un objeto state en el constructor del componente.

constructor (props) {
  super(props);
  this.state = {
    theStateCount: 0
  }
}

Se considera una mala practica sesear propiedades a estados en este método.

NOTA: para conocer más en detalle las propiedades y el estado de los componentes puedes echar un vistazo a este artículo.

 

componentWillMount

Este método es invocado justo antes de ejecutarse el método render(), y se ejecuta tanto del lado del cliente como del lado del servidor. Esto del lado del servidor es un tema interesante, pero me extendería demasiado y no es el objetivo del post, así que de momento lo dejaremos ahí.

Volviendo al tema, este método es muy util para manejar ciertos datos necesarios para la representación del componente o declarar ciertos eventos, aunque conviene tener en cuenta que al no haberse renderizado aún el componente, las referencias a sus elementos aún no están disponibles. Es decir, si por ejemplo nuestro componente “pinta” un formulario en el cual hay un elemento al que queremos modificar alguna propiedad via JavaScript, la referencia al elemento devolverá un error ya que el elemento del DOM correspondiente aún no existe.

 

render

Este método se encarga de “pintar” el marcado generado por el componente. Dicho marcado podrá ser marcado HTML estático, otro u otros componentes, null o false. Es en este punto del ciclo de vida donde las props y el state del componente son interpretados, por esto, ninguno de estos atributos deberían ser modificados en este punto.

 

componentDidMount

Una vez que el método render se ha ejecutado se invocará esta función. El DOM ya es accesible por lo que podremos realizar cualquier manipulación sobre él, de hecho, este el el lugar recomendado para ello. También es el método ideal para hacer peticiones a una API, entre otras muchas cosas.

 

class MyComponent extends React.Component {

  constructor ( props ) {
    super( props )

    this.state = {
      theStateCount: 0
    }
  }

  componentWillMount () {
    // Some code here
  }

  componentDidMount () {
    // Some code here
  }

  render () {
    return(<div className="my-component">
      <h2>Hi! I'm a React component</h2>
    </div>)
  }
}

MyComponent.defaultProps = { initialCount: 0 };

 

Con esto ya hemos visto los métodos que intervienen en el montaje de un componente, ahora veamos los que intervienen en los siguientes escenarios, la actualización del componente.

 

Actualización del estado de un componente

Los cambios de estado de un componente desencadenan la ejecución de los siguientes métodos.

 

actualizacion de estado de un componente react

 

shouldComponentUpdate

Se ejecuta siempre antes de render() y decide si nuestro componente se re-renderiza o no. Recibe dos parámetros, las nuevas propiedades y el nuevo estado y devuelve un valor boleano el cual determina si el componente se actualiza o no.

Este es el lugar idoneo para comprobar los nuevos datos y tomar la decisión que más convenga en cada caso.

 

componentWillUpdate

Se ejecuta tan pronto shouldComponentUpdate devuelve true, y al igual que el, recibe dos parámetros, las nuevas propiedades y el nuevo estado.

Este método está pensado para preparar al componente para su actualización por lo que se debe evitar modificar estados en este punto. Si se llama a setState desde este método dará lugar a que el componente se renderize en un bucle infinito que seguramente hará que te pete el navegador.

 

componentDidUpdate

Se ejecuta justo después de render() y al igual que ocurre con el método componentDidMount puede utilizarse para realizar operaciones en las que intervengan elementos del DOM.

 

class MyComponent extends React.Component {

    shouldComponentUpdate ( nextProps, nextState ) {
      // Some code here
      return true;
    }

    componentWillUpdate ( nextProps, nextState ) {
      // Some code here
    }

    componentDidUpdate ( prevProps, prevState ) {
      // Some code here
    }

    render () {
      return(<div className="my-component">
        <h2>Hi! I'm a React component</h2>
      </div>);
    }

  }

 

Actualización de las propiedades de un componente

Al igual que ocurre en el caso anterior las modificaciones de las propiedades de un componente desencadenan la ejecución de varios métodos.

 

actualizacion de propiedades de un componente react

 

componentWillReceiveProps

Se ejecuta tan sólo cuando las propiedades del componente cambian (Ojo! solo las propiedades no el estado). Recibe como parámetro las nuevas propiedades y nos permite actualizar el estado sin re-renderizar el componente.

Los pasos siguientes para este escenario a partir de este punto son iguales que cuando se actualiza el estado del componente.

class MyComponent extends React.Component {

   componentWillReceiveProps (nextProps) {
      // Some code here
    }

    shouldComponentUpdate (nextProps, nextState) {
      // Some code here
      return true;
    }

    componentWillUpdate (nextProps, nextState) {
      // Some code here
    }

    componentDidUpdate (prevProps, prevState) {
      // Some code here
    }

    render () {
      return(<div className="my-component">
        <h2>Hi! I'm a React component</h2>
      </div>);
    }

  }

 

 

Desmontaje de un componente

En el desmontaje de un componente React interviene un único método, componentWillUnmount, y es invocado justo antes de que el componente se desmonte. Es ideal para realizar operaciones de limpieza como eliminar listeners definidos en el método componentDidMount, temporizadores o demás objetos que puedan quedar en memoria.

class MyComponent extends React.Component {

  componentWillUnmount () {
     // Some code here
  }

}

 

Con esto ya hemos repasado los cuatro escenarios propuestos así como los métodos que intervienen en cada uno de ellos. Mi consejo, como siempre, es que experimentéis y probéis por vosotros mismos como se comportan los componentes en cada caso.

 

He preparado este Codepen para que juguéis un poco y os paséis un rato maltratando componentes. 

Hasta la próxima!

  • Pingback: Ciclo de vida de los componentes React()

  • Juan Castela Martínez

    Hola,

    Primero agradecerte tus posts y el tiempo invertidos en ellos. Tengo una pregunta sobre los componentes, ¿pueden pasarse valores entre distintos componentes que no sean padre-hijo?. No sé si me he explicado bien.

    Muchas gracias y saludos.

    • Hola Juan, gracias a ti por el interés.

      Existen varias técnicas para conseguir lo que comentas. La documentación oficial sugiere para estos casos que crees tu propio sistema global de eventos, pero tienes otras opciones como implementar Flux, la arquitectura que recomienda Facebook y de la que encontraras bastante info y alguna que otra librería bastante potente para hacer la tarea más fácil.

      Personalmente me está gustando mucho más usar Mobx y mobx-react, es una librería que implementa observables, lo que bajo mi punto de vista es súper potente ya que además de ahorrar bastante codigo y ganar en legibilidad, para aplicaciones grandes resulta mucho más fácil controlar errores. En breve espero escribir sobre el tema.

      Espero haberte sido de ayuda.
      Un saludo!

      • Juan Castela Martínez

        Hola Edgar,

        Muchas gracias por tu respuesta. Lo que indica en la documentación lo leí y es lo que estoy intentando hacer. Tengo un js con dos funciones que exporto, setData(valor) y getData(), cuando las invoco sé que funcionan porque soy capaz de sacar el valor por consola. El componente que tiene el getData recupera los valores porque si llamo a la función en el evento onClick del componente me carga los datos, pero no consigo hacerlo cuando se pinta.

        Voy a echarle un vistazo a las librerías que comentas para ver si consigo que funcione.

        Muchas gracias por tu ayuda y un saludo.

        • Pues con lo que comentas no te sabría decir que ocurre pero si subes un ejemplo a jsfiddle o a codepen le echo un ojo.
          Saludos!

          • Juan Castela Martínez

            Muchas gracias por tu interés Edgar. Voy a intentar un par de cosas más, y si veo que no sale, te pido ayuda. Así, practicando, es como mejor se parende.

            Muchas gracias por tu interés, de verdad.

  • Alvaro Royo Alvarez

    Hola Edgar, estoy pensando en montar un blog sobre swift de lo que voy aprendiendo y demás y he pensado fijarme en el tuyo que me parece un diseño muy limpio y muy comprensible.

    Pd: A ver si te dejas ver que hace unos viernes fui por alli y no estabas…!!

    • Hol Alvaro!
      Pues si macho, a ver si nos vemos un viernes de estos y nos contamos…

      El tema me lo he montado yo, te lo puedo pasar si quieres y te ahorras el curro!