El ciclo de vida de un componente de React tiene tres fases: el montaje (el componente aparece en pantalla por primera vez), la actualización (vuelve a renderizarse porque cambiaron su estado, sus props o su contexto) y el desmontaje (se quita). En los componentes de función reaccionas a estas fases con useEffect: un efecto con [] se ejecuta después del montaje, un efecto con dependencias se ejecuta después de las actualizaciones en las que cambiaron, y la función que devuelve un efecto se ejecuta al desmontar.
La consola bajo la vista previa muestra cada fase cuando ocurre.
La consola empieza con render, count = 0 y mounted. Cada clic en Count registra un renderizado y una actualización. Unmount registra unmounted; Mount vuelve a empezar desde count = 0, porque el estado se descartó junto con el componente.
Montaje
Un componente se monta la primera vez que React lo renderiza en un lugar concreto del árbol. React llama a la función, crea los nodos del DOM a partir del JSX que devuelve, los inserta, deja que el navegador pinte y luego ejecuta los efectos del componente.
useEffect(fn, []) es donde inicias las cosas que deben existir mientras el componente esté en pantalla: una suscripción, un temporizador, una conexión, una primera petición de datos. Se ejecuta después del pintado, así que nunca retrasa el primer frame.
Actualización
Un componente se actualiza (vuelve a renderizarse) cuando:
- cambia su propio estado,
- su padre vuelve a renderizarse (por defecto, todos los hijos también renderizan, cambien o no sus props),
- cambia un contexto que lee.
Después de cada actualización, React ejecuta los efectos cuyas dependencias cambiaron. Antes de volver a ejecutar un efecto, ejecuta la limpieza anterior de ese efecto, así que la suscripción vieja se cierra antes de que se abra la nueva. La página de renderizado cubre con más detalle qué provoca un renderizado.
Desmontaje
Un componente se desmonta cuando su padre deja de renderizarlo (como hace {show && <Counter />} arriba), cuando cambia su key, o cuando se desmonta la propia raíz. React quita sus nodos del DOM, ejecuta la limpieza de cada efecto y descarta su estado. Si el componente vuelve a aparecer más tarde, es una instancia completamente nueva.
Padres e hijos
React renderiza de arriba abajo pero ejecuta los efectos de abajo arriba. Cuando un padre y un hijo se montan juntos, los efectos del hijo se ejecutan primero, así que el efecto de un padre puede contar con que sus hijos ya están completamente configurados.
El montaje registra render Parent, render Child, Child effect, Parent effect. Pulsa Remove y las limpiezas se ejecutan al revés: Parent cleanup y luego Child cleanup.
Fases de render y commit
Cada actualización pasa por dos fases:
- Fase de render. React llama a tus componentes para calcular el nuevo JSX y lo compara con el resultado anterior. Todavía no cambia nada en pantalla. React puede pausar este trabajo, ejecutarlo dos veces en StrictMode o descartarlo, y por eso renderizar debe ser puro: nada de peticiones, suscripciones ni cambios en variables externas.
- Fase de commit. React aplica las diferencias al DOM, conecta las refs y ejecuta los callbacks de
useLayoutEffect, todo antes de que el navegador pinte. Los callbacks deuseEffectse ejecutan después, normalmente cuando el navegador ya pintó. Cuando un clic u otra entrada discreta causó la actualización, React los ejecuta justo después de los layout effects, para que el resultado esté listo antes del siguiente evento.
Cada clic registra las tres líneas en el mismo orden. Estos efectos no tienen array de dependencias, así que se ejecutan después de cada renderizado. Agrega [] como segundo argumento a ambos y se ejecutan solo después del primero.
Métodos de ciclo de vida de las clases y sus equivalentes en hooks
Los componentes de clase expresan el ciclo de vida como métodos con nombre. Los encontrarás en código antiguo y en los error boundaries, que todavía necesitan una clase.
| Método de clase | Cuándo se ejecuta | Equivalente en componentes de función |
|---|---|---|
constructor | una vez, antes del primer renderizado | useState(initial), useRef |
static getDerivedStateFromProps | antes de cada renderizado | calcular el valor durante el renderizado, o reiniciar con una key |
render | en cada renderizado | el cuerpo de la función |
componentDidMount | cuando el primer renderizado está en el DOM | useEffect(fn, []) |
shouldComponentUpdate | antes de una actualización, puede saltarla | React.memo (responde lo contrario: "¿las props son iguales?") |
getSnapshotBeforeUpdate | justo antes de que cambie el DOM | no hay un hook directo; lo más parecido es useLayoutEffect con una ref |
componentDidUpdate | después de cada actualización | useEffect(fn, [deps]) (también se ejecuta al montar) |
componentWillUnmount | antes de quitarse | la limpieza devuelta por useEffect |
getDerivedStateFromError, componentDidCatch | cuando un hijo lanza un error | ninguno: escribe una clase error boundary |
Aquí tienes una clase que registra cada método cuando se ejecuta:
El montaje registra constructor, getDerivedStateFromProps, render, componentDidMount. Un clic registra getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, componentDidUpdate. Haz que shouldComponentUpdate devuelva false y los clics dejan de actualizar el botón: el estado cambia, pero React se salta el renderizado. La página de componentes de clase muestra cómo convertir una clase así en una función.
Piensa en sincronización, no en ciclo de vida
La tabla de hooks es útil para leer código antiguo, pero puede llevarte a escribir efectos que copian hábitos de las clases, como un gran efecto "did mount" y un gran efecto "did update". Un efecto se entiende mejor como "mantén esta cosa externa sincronizada con estos valores": conéctate a roomId, y cuando roomId cambie, desconéctate y vuelve a conectarte. Escribe un efecto por cada cosa que sincronizas, incluye como dependencias los valores que usa, y deja que React decida cuándo ejecutarlo.
Preguntas frecuentes
¿Cuáles son las fases del ciclo de vida de un componente de React?
El montaje (React crea el componente y pone su resultado en pantalla), la actualización (vuelve a renderizarse porque cambiaron su estado, sus props o su contexto) y el desmontaje (se quita y hace su limpieza).
¿Cuál es el equivalente en hooks de componentDidMount?
useEffect(() => { ... }, []). El array de dependencias vacío hace que se ejecute una vez, después de que el primer renderizado está en pantalla. La función que devuelve es el equivalente de componentWillUnmount.
¿Cuál es el equivalente en hooks de componentDidUpdate?
useEffect con los valores que te interesan en el array de dependencias, como [userId]. A diferencia de componentDidUpdate, también se ejecuta después del primer renderizado, así que cubre el montaje y la actualización juntos.
¿En qué orden se ejecutan los efectos del padre y del hijo?
React renderiza del padre hacia abajo, pero ejecuta los efectos de los hijos hacia arriba: el efecto de un hijo se ejecuta antes que el de su padre. Al desmontar, la limpieza del padre se ejecuta antes que la del hijo.
¿Qué son las fases de render y commit?
En la fase de render React llama a tus componentes para calcular cómo debe verse la pantalla; puede pausar o descartar este trabajo. En la fase de commit aplica los cambios al DOM y ejecuta los layout effects; los efectos normales se ejecutan después, normalmente cuando el navegador ya pintó.