Los React Hooks useEffect
y useLayoutEffect
son efectos que protegen partes de nuestros componentes para evitar que se ejecuten en cada render, sino únicamente cuando realmente los necesitamos.
Efectos vs. ciclo de vida
Usando clases y React.Component
hacíamos este mismo trabajo con los métodos del ciclo de vida: componentDidMount
, componentDidUpdate
y componentWillUnmount
.
Usando funciones, efectos y React Hooks podemos replicar este comportamiento de la siguiente forma con useEffect
:
useEffect(() => {
/*
componentDidMount
Para que se ejecute alguna acción solo cuando se monta el componente en pantalla, el
segundo parámetro de useEffect debe ser un Array vacío.
*/
/*
componentDidUpdate
El componente se actualiza solo si el segundo parámetro de useEffect es un array con
el valor que se espera que cambie.
*/
return () => {
/*
componentWillUnmount
Aquí describes todo evento al que hayas suscrito el componente, de no haberlo
hecho, no es necesario agregar el return al final de la función.
*/
}
},[])
También podemos usar useLayoutEffect
. Es muy similar a useEffect
, aunque no los usamos muy seguido debido que tienen una pequeña diferencia: useLayoutEffect
es un llamado de manera síncrona después de todas las mutaciones del DOM (profundizaremos esto más adelante), mientras que useEffect
no.
Si es la primera vez que escuchas sobre este hook verás que en código es lo mismo que su compañero, solo cambia el nombre:
useLayoutEffect(() => {
return () => {
}
}, [])
💡 Puedes tener múltiples hooks de efecto en un solo componente
Diferencias entre useEffect y useLayoutEffect
useEffect
Este hook se ejecuta de manera asíncrona después de ser renderizado y mostrado el componente en pantalla.
Este es el paso a paso que sucede con tu componente cuando estás usando este hook:
- El estado del componente cambia
- El componente se vuelve a renderizar
- El componente es mostrado en pantalla
- useEffect se ejecuta
useLayoutEffect
Por otro lado, useLayoutEffect se ejecuta de manera síncrona después de que se tenga el render del componente, pero ANTES de ser pintado en pantalla.
⚠️ Todo lo que hagas con este hook hará que el paint del componente tarde más de lo esperado, lo cual puede afectar el performance.
Ahora verás que pasa con tu componente:
- El estado del componente cambia
- El componente se vuelve a renderizar
- useLayoutEffect se ejecuta y React espera a que termine
- El componente es mostrado en pantalla
¿Cuándo usar cada uno?
Hay un par de consideraciones al momento de usar uno u otro, recuerda:
- Si necesitas manipular algún elemento del DOM antes de que sea mostrado en pantalla, useLayoutEffect te servirá mucho
- Si requieres que tus componentes se vean fluidos y no presentan una especie de “parpadeo” cada que cambia el estado, usa useLayoutEffect (con precaución)
- Y de manera muy general, useEffect te será útil para todos los demás casos
¡Manos a la obra!
Vamos a poner en práctica ambos React Hooks con un par de ejemplos para estudiar mejor sus diferencias.
Manipula elementos del DOM antes de que sean mostrados
React soporta el agregar manejadores de eventos para el click
, focus
, select
directamente a los componentes de la siguiente forma:
Aunque también existen alternativas como hacer un addEventListener
al elemento deseado (sí, justo como lo haríamos en vanilla JS). Pero en React no podemos hacer algo como document.querySelector('#button')
, ya que no es lo ideal. En cambio, tenemos useRef
otro React Hook que nos permite acceder a elementos del DOM de manera sencilla.
Vamos a crear un componente como cualquier otro y con useRef
junto con useLayoutEffect
agregaremos manejadores de eventos.
import { useRef, useLayoutRef, useState } from'react'
exportconst MyDummieComponent = () => {
const [state, setState] = useState()
const printDate = () => {
//La función hará algo muy sencillo, imprime en pantalla la fecha actual
setState(newDate().toTimeString())
}
useLayoutEffect(() => {
//Al agregarlos con LayoutEffect nos aseguramos que los manejadores de eventos
//estén disponibles una vez el boton sea mostrado
buttonRef.current.addEventListener("click", printDate)
return() => buttonRef.current.removeEventListener("click", printDate)
// En este caso agregarás la función de limpiado para remover el eventlistener
// una vez desaparezca el componente
}, [])
const buttonRef = useRef(null)
return(
<div>
<h1>{state}h1>
<buttonref={buttonRef}> Check date button>
div>
)
}
🧑💻 Visita el ejemplo en Codepen
Evita que tus componentes tengan una alteración visual cuando cambian de estado
Si has tenido que cambiar múltiples veces el estado de algún componente en un corto periodo de tiempo, tal vez hayas notado que tiene una ligera variación cuando usas useEffect. Afortunadamente, hay una alternativa para corregir esta situación. Haz un componente dummie que solo te de un número aleatorio cada que hagas click sobre él.
Para percibir de mejor manera el cambio que se tiene entre estos dos hooks, te apoyarás de un interval
que ejecutará el cambio de estado del componente.
import { useState, useLayoutEffect } from'react'
exportconst RandomNumbers = () => {
// Inicializa el estado de nuestro componente
const [number, setNumber] = useState(0)
//En este caso usarás el segundo hook, intenta ver el cambio que tiene ejecutándolo
//con useEffect
useLayoutEffect(() => {
//Aquí solo te aseguras que el efecto se efectúe solo si el number es 0
if(number === 0){
setNumber(10 * Math.random() * 200 + Math.random())
}
// En el array ponemos el estado para ejecutar el hook cuando cambie
}, [number])
const createInterval = () => {
// este interval ayudará a ver mejor lo que pasa con useLayoutEffect y useEffect
let intervalTest = setInterval(() => {
setInterval(0)
}, 10)
// El timeout es para evitar problemas con el interval y dejar de ejecutarlo a los
// 10 segundos
setTimeout(() => {
clearInterval(intervalTest)
},10000)
}
return(
<div>
<p>{number}p>
<buttononClick={() => setNumber(0)}>Change numberbutton>
<buttononClick={createInterval}>Run testbutton>
div>
)
}
Visita el Codepen con
useEffect
.
Visita el Codepen conuseLayoutEffect
.
Juega un poco con este componente y verás esa pequeña diferencia que existe entre un hook del otro, solo debes cambiar useLayoutEffect de useEffect y viceversa. 😉
Conclusiones
Como te habrás dado cuenta, useLayoutEffect
solo lo usarás en muy contadas ocasiones y estará ahí para auxiliarte cuando tengas problemas con useEffect
o cuando necesites acceder a un nodo del DOM antes de que sea mostrado en pantalla.
Si quieres aprender React.js de manera profesional, te recomiendo la siguiente ruta de cursos donde conocerás los React Hooks mucho más a fondo y con proyectos extremadamente prácticos:
- Curso de Introducción a React.js
- Curso de React.js: Patrones de Render y Composición
- Curso Práctico de React.js
- Curso Profesional de React Hooks
- Curso de React Avanzado
#NuncaParesDeAprender
Curso de React.js