@composi/core:

Ciclo de Vida

Enganches de Ciclo de Vida

Con Composi, los componentes funcionales tienen tres enganches de ciclo de vida que puedes usar:

Los enganches de ciclo de vida te permiten realizar tareas en un momento específico de la vida del componente. También te permiten crear animaciones cuando cambia el estado de un componente.

onmount

El enganche de ciclo de vida onmount se activa justo después de que el componente se monta en el DOM. Toma un callback cuyo primer argumento es el elemento base del componente. Esto le permite hacer algunas cosas útiles, como acceder a los elementos secundarios del componente, o configurar escuchas de eventos y eventos delegados.

En la documentación de props y events, vimos cómo crear una lista. Ahora vamos a ver cómo agregar elementos a la lista. Para hacer eso, necesitamos tener un input donde el usuario pueda agregar un nuevo ítem y un botón para hacer clic para agregar ese ítem a la lista. El enganche onmount es una forma de acceder al input del componente para obtener su valor. Entonces, hagamos esto. Vamos a cambiar el ejemplo, envolviendo la lista en un div que tiene dos hijos, la lista y un párrafo con un input de texto y un botón.

Acceder al DOM del Componente

Para poder acceder al input del componente, debemos agregar un enganche de ciclo de vida onmount . Lo ponemos directamente en el input. De esa manera, el enganche onmount obtiene una referencia al elemento input mismo. Luego podemos guardar esa referencia en el objeto refs para un fácil acceso en el código del componente.

También estamos agregando enganches onmount a los ítemes de la lista con el único propósito de animarlos cuando se crean. Añade nuevos ítemes a la lista para ver la animación.

See the Pen @composi/core - Ciclo de Vida-onmount by Robert Biggs (@rbiggs) on CodePen.

onupdate

A veces vas a querer saber cuándo un componente se está actualizando, como cuando se cambian sus props. Puedes realizar esto con onupdate. Este enganche recibe tres argumentos: el element base del componente, las props anteriores y las props nuevas. Puedes comparar las props para ver cómo cambiaron y así decidir que hacer.

onuptate se invoca cuando el element particular en el cual está registrado se acutaliza. El enganche onupdate no es invocado cuando el elemento se monta or desmonta.

Para usar onupdate correctamente, tienes que entender bien qué son las props viejas y nuevas. Pasamos las props a un componente cuando pasamos éste a la función code>render:

render(<Lista datos={frutas}/>, 'body')

Decimos que el atributo datos es una prop. Pero, en este caso no va persistir como prop. Eso es porque la función render convertirá la etiqueta JSX en una función y sus props serán los argumentos de aquella función. Tecnicamente, las props son los valores agregados directamente en los elementos que el componente como función devuele. Vamos a examinar otra vez el componente de Lista:

function Lista({datos}) {
  function mostrarCambio(elemento, propsViejas, propsNuevas) {
    // compara propsViejas con propsNuevas
  }
  return (
    <ul onupdate={mostrarCambio}>
      {
        datos.map(ítem => <li key={ítem.clave}>{ítem.valor}</li>)
      }
    </ul>
  )
}
render(<Lista datos={frutas}/>, 'body')

En el ejemplo anterior, fíjate que hemos puest el evento onupdate directamente en el elemento base del componente. Luego, en nuestra función mostrarCambio queremos comparar las props viejas y nuevas para ver qué cambia. Podrías estar pensando que obtendremos las versiones anterior y nueva de la propiedad datos. Ahora mira el elemento. ¿Ves otras propiedades en él además de onupdate? De hecho no hay y esto es un problema. El no tiene propiedades. Los datos de frutas que queremos probar vienen siendo un parámetro y los estamos utilizando en el código en un bloque delimitado por llaves. Esto significa que cuando intentemos acceder a propsNuevas y propsViejas, estas estarán indefinidas. Para habilitar el enganche de ciclo de vida onupdate para acceder a los datos, debemos registrar los datos como una propiedad directamente en el elemento donde está el gancho del ciclo de vida. Aquí está el componente con esa corrección:

function Lista({datos}) {
  function mostrarCambio(element, propsViejas, propsNuevas) {
    // Compara propsViejas con propsNuevas
  }
  // Pon los datos directamente en la etiqueta ul:
  return (
    <ul onupdate={mostrarCambio} datos={datos}>
      {
        datos.map(ítem => <li key={ítem.clave}>{ítem.valor}</li>)
      }
    </ul>
  )
}

Este cambio sutil para exponener los datos al enganche update significa que ahora tenemos acceso al estado anterior y actual de datos. Con eso, podemos comparar propsViejas.datos con propsNuevas.datos para ver qué ha cambiado.

Ten cuidado de que siempre estás exponiendo las propiedades que necisitas en el miso elemento en el cual colocas onupdate. Si no, los valores salirán no definidos (undefined).

En el ejemplo siguiente mostramos cómo usar onupate para mostrar un mensaje de las diferencias entre el valor de cuenta mientras va aumentando o disminuyendo. Por usar propsViejas y propsNuevas, podemos ver cómo el valor cambia y entonces mostra esa diferencia en la consola.

See the Pen @composi/core - ejemplo de actualización by Robert Biggs (@rbiggs) on CodePen.

Es importante saber cuándo se debe de usar onupdate. El caso de uso para ello es muy limitado. Es útil si deseas ver cómo una propiedad ha cambiado. Nunca hay que utilizarlo para cambiar datos, ya que eso activaría otra actualización y te pondría en un bucle infinito. Puede usarlo para activar una animación. Durante la actualización, agregue una clase que cause la animación, y luego elimine la clase. También se puede usar onupdate para poner focus en un element de form, como un input de texto, etc.

onunmount

A veces, vas a querer saber cuando se está eliminando un componente del DOM para poder hacer algo. Tal vez necesites realizar una limpieza de ambiente o desencadenar algún otro evento o función. onunmount te permite hacer eso. De hecho, onunmount le permite interceptar el proceso de desmontaje y demorarlo hasta que hayas terminado con lo que sea que estás haciendo. Esto es ideal para situaciones en las que deseas hacer una animación cuando se elimina un ítem de una lista. Haremos una lista simple que usa onunmout para animar el ítem de lista antes de eliminarlo. Para esto, omitiremos la parte que realmente elimina un ítem de lista ya que ya cubrimos eso en el ejemplo anterior para la delegación de eventos.

onunmount expects that its callback with handle a done() function. Specifically, onunmount gets passed two arguments: the element being deleted, and a done callback to invoke when you are done. If you fail to provide a done() callback in your code or forget to invoke at the end of your code, the element will not be removed from the DOM, which could result in really unexpected results as the list data is mutated.

onunmount espera que su callback va tratar con una función done(). Específicamente, onunmount pasa dos argumentos: el elemento que se está eliminando y el callback done para invocar cuando haya terminado. Si no proporcionas un callback done() en su código u olvidas invocarlo al final del código en esa función, el elemento no se eliminará del DOM, lo que podría causar resultados realmente inesperados mientras se cambian los datos de aquella lista.

En el ejemplo próximo usamos onunmount para implementar una animación de keyframe antes de que el ítem de lista sea eliminado. También usamos onmount en los ítemes de lista para hacer la animación de los ítemes nuevos mientras se trasladan en la vista.

See the Pen @composi/core - Ciclo de Vida-onunmount by Robert Biggs (@rbiggs) on CodePen.

Argumentos para Ciclos de Vida

Los enganches de ciclo de vida reciben sus argumentos automáticamente, así pues puedes utilizar sus gestores sin argumento en los eventos en línea. Cada enganche de ciclo de vida espera argumentos diferentes, pero puedes indicarlos en su gestor en el evento en línea:

function Lista() {
  function init(base) {
    // Haz algo con el elemento base del componente.
    base.querySelector('input').focus()
  }
  function mostrarCambio(element, propsViejas, propsNuevas) {
    // El elemente en el cual se ubica el evento, en este caso, la etiqueta ul.
    // Compara los valores de propsViejas con los nuevos.
    if (propsNuevas.data.length > propsViejas.data.length) {
      console.log('Se agregó un ítem a la lista.')
    } else {
      console.log('Se eliminó un ítem de la lista.')
    }
  }
  function gestionarDesmonte(element, done) {
    // Haz algo con el elemento base del componente.
    // Entonces desencadena la animación de eliminación.
    element.classList.add('deleting')
    // Then let the component unmount.
    // Since we've set the animation to last 3 seconds,
    // we need to delay the unmounting for 3 seconds:
    // Entonces deja que el componente desmonte.
    // Pueste que hicimos que la animación dure tres segundos,
    // tenemos que demorar el desmontar por tres segundos.
    setTimeout(() => {
      done()
    }, 3000)
  }
  return (
    <ul onmount={init} onupdate={mostrarCambio} onunmount={gestionarDesmonte} data={data}></ul>
  )
}

Lo que hay que tener en cuenta con onunmount es que causa que el desmontar sea asincrónico. Aunque el algoritmo de parche ha identificado una condición que requiere que el elemento se elimine del DOM, espera hasta que invocas done() en el gestor para terminar la eliminación del elemento.