Qué es el ciclo de vida de los componentes, por qué es tan importante en el desarrollo con la librería Lit y en Web Components en general. Qué métodos del ciclo de vida tenemos disponibles en el estándar y en el desarrollo con Lit.
El ciclo de vida es una de las herramientas que tenemos para ejecutar código en el preciso momento que se necesite, durante la vida de un componente. Por ejemplo, conseguir que el componente reaccione cuando se ha inicializado, cuando se usa en un documento HTML, cuando alguien manipula una de sus propiedades, etc.
Este tema es fundamental para el desarrollo de componentes y poder realizar comportamientos de todo tipo. De hecho, aunque hasta ahora en el Manual de Lit hemos hecho solo componentes sencillos, ya nos hemos visto obligados a implementar algunos métodos del ciclo de vida.
Qué es el ciclo de vida de los componentes
Básicamente es un resumen de los estados o situaciones por las que puede pasar un componente durante todo el tiempo en el que está presente en una página web. Incluso antes de estar presente en el documento pasa por estados de su ciclo de vida.
Las funciones callback del ciclo de vida de los componentes nos permiten como desarrolladores definir código, para que se ejecute en el preciso instante que un elemento pasa por algún hito de su ciclo de vida.
Qué son funciones callback
Las funciones callback son funciones que se definen en algún lugar del código y se ejecutan de manera automática al producirse ciertas situaciones. Por ejemplo, podemos tener una llamada Ajax y ejecutar una función callback cuando se ha recibido el resultado.
El concepto de callback viene de Javascript en general, no de Web Components o jQuery. Simplemente son funciones que se llaman de vuelta. Se suelen usar en la programación asíncrona para conseguir realizar procesos cuando terminan los procesos asíncronos. En este artículo puedes encontrar más información sobre las funciones callback en Javascript.
Pues bien, nosotros podemos asociar funciones callback a los métodos del ciclo de vida, que se ejecutarán en el momento en el que éstos están siendo utilizados y vayan recorriendo esas etapas marcadas por el ciclo de vida.
Cuáles son los métodos del ciclo de vida de los componentes Lit
En componentes Lit disponemos de dos grupos de métodos del ciclo de vida. Son los siguientes:
- Métodos del ciclo de vida nativo de Web Components: El propio estándar Web Components define una serie de métodos del ciclo de vida. Dado que con Lit creamos componentes estándar, debemos hacer uso de los métodos del ciclo de vida nativos para los comportamientos en los que el estándar ya tiene una solución incorporada.
- Métodos del ciclo de vida incorporados por Lit: de manera adicional, Lit incorpora algunos métodos de ciclo de vida extra, que nos permiten hacer cosas que no están implementadas en el estándar y que tienen que ver específicamente con el uso de las propiedades reactivas de los componentes Lit y la actualización del template.
Métodos nativos del ciclo de vida
Primero que todo te vamos a dar una referencia a un artículo que explica cuáles son los métodos del ciclo de vida de Web Components, ya que ahí los tienes todos perfectamente explicados.
Por enumerarlos, son los siguientes:
- constructor: Básicamente se ejecutan cuando los componentes se deben inicializar.
- connectedCallback: se ejecuta cuando el componente se usa, porque se incorpore al documento HTML o al template de otro componente.
- disconnectedCallback: cuando el elemento se retira del DOM o Shadow DOM.
- attributeChangedCallback: Cuando el atributo de un componente cambia.
- adoptedCallback: cuando el componente cambia a otro documento.
Dentro de estos métodos nativos del ciclo de vida son especialmente útiles el constructor
, connectedCallback
y disconnectedCallback
. Hemos visto ejemplos del constructor y ahora veremos algún ejemplo de los otros dos.
Además tenemos attributeChangedCallback
que en Lit no se suele usar, porque justamente los métodos del ciclo de vida de Lit vienen a cubrir y extender las funcionalidades de este método, y adoptedCallback
que es algo muy raro y avanzado.
Para ser sincero, nunca he necesitado usar adoptedCallback, por lo que no vamos a ver ejemplos en este manual.
Métodos del ciclo de vida incorporados por Lit
La librería Lit incorpora además otros métodos del ciclo de vida que son propios y que, como decía, vienen a cubrir casos extra relacionados con la reactividad de los componentes incorporada en la librería.
Estos métodos son bastante más avanzados y los vamos a cubrir con detalle más adelante, mostrando ejemplos relevantes, pero vamos a citar aquí los que se usan más a menudo.
- firstUpdated: este método se ejecuta la primera vez que el template de un componente se ha actualizado. Por tanto, tenemos certeza que ya se ve algo en la página y que todas las propiedades cuyos valores se han seteado en los atributos están ya con los valores sincronizados.
- updated: se invoca cuando el componente ha terminado de actualizarse, lo que ocurre cada vez que una de sus propiedades reactivas a provocado un campo en el template.
- updateComplete: esto no es un método sino una promesa que termina cuando el método ha terminado de actualizar su template.
El ciclo de vida de los componentes Lit es bastante sofisticado, por lo que solo hemos visto nombres de algunos métodos que deberían comenzar a sonarte. Más adelante los explicaremos y veremos ejemplos.
Manejadores de eventos y el ciclo de vida
Vamos a ver ahora un uso muy importante de los métodos del ciclo de vida, en concreto de los métodos nativos del estándar de Web Components, que debemos aplicar cada vez que un componente haga uso de manejadores de eventos que no son asignados de manera declarativa en el template del componente.
Cuando los manejadores de eventos son declarados en el template, la propia librería Lit se encarga de asociarlos a los elementos y retirarlos cuando el componente deja de existir, por lo que no debemos preocuparnos de mucho. Pero si nosotros de manera imperativa queremos definir eventos, debemos de ser cuidadosos para que estos eventos no permanezcan en ejecución cuando el componente deja de existir. Debido a esta importante situación vamos a comenzar por aquí el tema del ciclo de vida.
¿Cuándo necesitamos definir eventos fuera de los templates?
Esto se hace muy a menudo y para infinidad de situaciones. Pero sobre todo lo harás cuando los manejadores no los quieres asociar a partes del shadow DOM definidas en el template, sino sobre la propia etiqueta host del componente, sobre los elementos hijos que hay en el DOM local del componente, o sobre elementos padre u objetos como document
o window
.
Por ejemplo, imagina un componente que está atento a situaciones en las que el usuario redimensiona la ventana del navegador. Entonces tendrá que crear eventos en el objeto window
. O un componente que define eventos a ejecutar cuando se hace scroll en la página, del mismo modo tendrá que definir el manejador sobre el objeto window.
Pero también puede que necesitemos definir comportamientos periódicos, como un auto-save en un componente de formulario, que envía los datos al servidor cada minuto. En un caso especial como éste puede ocurrir que el auto-save siga en marcha incluso aunque el componente ya no se encuentra en la página. Todo eso lo tenemos que controlar.
Ejemplo de manejadores definidos en conectedCallback y disconnectedCallback
Vamos a mostrar un ejemplo sencillo de definición de eventos fuera del template. Consiste en un componente que informa de la posición del ratón cuando se hace clic en cualquier parte del documento.
Ese informe de la posición del clic lo tenemos que hacer como un evento asignado al documento, ya que queremos detectar los clics en cualquier lugar de la página.
Nuestro componente es muy sencillo. Lo vamos a ver todo de una vez:
import { LitElement, html, css } from 'lit';
const showClickPosition = (e) => {
console.log('Has hecho clic en ', e.clientX, 'x', e.clientY);
}
export class DwEventAddRemove extends LitElement {
static styles = [
css`
:host {
display: block;
}
`
];
connectedCallback() {
super.connectedCallback();
document.addEventListener('click', showClickPosition);
}
disconnectedCallback() {
super.disconnectedCallback();
document.removeEventListener('click', showClickPosition);
}
}
customElements.define('dw-event-add-remove', DwEventAddRemove);
- Definimos una función manejadora de evento que hemos llamado
showClickPosition
. Esta función la hemos definido fuera del componente, ya que no necesitamos que haga nada en concreto con el componente. - Definimos el evento en el
connectedCallback
para que se active en el momento en el que el componente pasa a formar parte del DOM. - Realizamos el borrado del manejador de evento con
removeEventListener()
al momento deldisconnectedCallback
, ya que cuando el elemento deje de estar en la página deseamos que dejen de mostrarse las posiciones del clic.
Es interesante apreciar que removeEventListener()
requiere recibir el nombre de la función que se debe eliminar de los manejadores de eventos. Por este motivo también habíamos definido esa función de manera externa, para que podamos acceder a ella tanto a la hora de hacer el addEventListener
como del removeEventListener
.
Para probar este ejercicio necesitarás quitar el elemento del DOM. Esto lo puedes hacer fácilmente desde las herramientas de desarrolladores de tu navegador. Para ello, desde la pestaña "Elements" de las herramientas de desarrolladores, haciendo clic derecho en el elemento que deseas borrar, seleccionas "Delete element".
Como práctica para ti, te sugerimos observar qué pasaría si el manejador del evento no lo retiramos mendiante el callback disconnectedCallback
, para darte cuenta de la importancia de usar este método del ciclo de vida del componente.
Conclusión a la introducción al ciclo de vida de los componentes Lit
Esta ha sido una sencilla introducción al ciclo de vida de los componentes en general, basados en la librería Lit y por tanto también en el estándar de Web Components. Hemos empezado por ejemplos sencillos pero que creo que son bastante relevantes sobre lo que respecta al desarrollo de componentes en la vida real.
En futuros artículos vamos a seguir profundizando en el ciclo de vida, ya que es un punto fundamental en el día a día del desarrollo de componentes.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...