Diferencias entre useEffect vs useLayoutEffect en React

¿Qué son los efectos secundarios en React?

Un efecto secundario es cualquier acción que ocurre dentro de tu aplicación que no está directamente relacionada con la representación de la interfaz de usuario. Por ejemplo:

  Efectos secundarios en React

  • Enviar solicitudes HTTP a servidores.
  • Almacenar datos en el almacenamiento del navegador.
  • Configurar funciones de tiempo como setTimeout o setInterval.

Estos efectos secundarios no modifican directamente la interfaz de usuario, lo que significa que React no vuelve a renderizar el componente en estos casos.

React ofrece hooks como useEffect, useLayoutEffect y useInsertionEffect para manejar efectos secundarios. El más comúnmente utilizado es useEffect.

useEffect vs useLayoutEffect: Diferencias Clave

CaracterísticauseEffectuseLayoutEffect
EjecuciónAsincrónica tras el renderizado.Sincrónica después de las mutaciones del DOM.
Momento de ejecuciónTras pintar el navegador.Antes de que el navegador pinte.
Uso principalOperaciones como solicitudes de API o subscripciones.Mediciones y modificaciones del DOM.
RendimientoMejor para efectos que no bloquean la pintura.Puede bloquear la pintura si es costoso.

¿Qué es useEffect?

useEffect permite realizar efectos secundarios después de que el componente se haya renderizado. El useEffect combina las funcionalidades de tres métodos del ciclo de vida de los componentes en React: cuando se crea el componente, se actualiza y se destruye. Esto permite replicar su comportamiento en componentes funcionales. Es ideal para tareas asincrónicas como:

  • Obtener datos de una API.
  • Configurar suscripciones.
  • Cambiar manualmente el DOM.

Sintaxis básica de useEffect

jsx
import { useEffect } from 'react';

function MyComponent() {
useEffect(() => {
  console.log('Componente montado.');

  // Cleanup opcional
  return () => {
    console.log('Componente desmontado.');
  };
}, []); // Dependencias vacías, se ejecuta solo al montar.

return <div>Hola mundo</div>;
}

Ejemplo práctico: Solicitud a una API


jsx
import { useEffect, useState } from 'react';

function FetchData({ userId }) {
const [userData, setUserData] = useState(null);

useEffect(() => {
  const fetchData = async () => {
    const response = await fetch(`https://api.example.com/users/${userId}`);
    const data = await response.json();
    setUserData(data);
  };

  fetchData();
}, [userId]);

return (
  <div>
    {userData ? (
      <div>
        <h1>{userData.name}</h1>
        <p>{userData.email}</p>
      </div>
    ) : (
      'Cargando...'
    )}
  </div>
);
}

¿Cuál es el problema con useEffect?

El principal inconveniente de useEffect es que se ejecuta de forma asincrónica y solo después de que el componente ha sido montado. Esto significa que no es adecuado para manejar efectos secundarios que dependen del diseño del componente o del estado del DOM inmediatamente después del renderizado.

¿Qué es useLayoutEffect?

useLayoutEffect se ejecuta de forma sincrónica después de las mutaciones del DOM, pero antes de que el navegador pinte la pantalla.

El hook useLayoutEffect se ejecuta de forma sincrónica, inmediatamente después de que React haya realizado las mutaciones necesarias en el DOM, pero antes de que el navegador pinte la pantalla. Comparte la misma API y sintaxis que useEffect.

Este hook se creó para abordar problemas específicos relacionados con efectos secundarios que requieren acceso inmediato al DOM actualizado, lo que useEffect no puede garantizar debido a su naturaleza asincrónica. Esto lo hace adecuado para:

  • Mediciones del DOM.
  • Realizar ajustes visuales inmediatos.

Sintaxis básica de useLayoutEffect

jsx
import { useLayoutEffect } from 'react';

function MyComponent() {
useLayoutEffect(() => {
  console.log('Se ejecuta antes del pintado del navegador.');
}, []);

return <div>Hola mundo</div>;
}

Medir el tamaño de un elemento

jsx
import { useLayoutEffect, useRef, useState } from 'react';

function MeasureDiv() {
const divRef = useRef(null);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

useLayoutEffect(() => {
  const { width, height } = divRef.current.getBoundingClientRect();
  setDimensions({ width, height });
}, []);

return (
  <div>
    <div ref={divRef} style={{ width: '200px', height: '100px', background: 'lightblue' }}>
      Mídeme
    </div>
    <p>Dimensiones: {dimensions.width}x{dimensions.height}</p>
  </div>
);
}

Ejemplos comparativos


1. Animación con useEffect

jsx
import { useEffect, useState } from 'react';

function AnimationEffect() {
const [opacity, setOpacity] = useState(0);

useEffect(() => {
  const interval = setInterval(() => {
    setOpacity((prev) => (prev < 1 ? prev + 0.1 : 0));
  }, 100);

  return () => clearInterval(interval);
}, []);

return <div style={{ opacity, background: 'blue', width: '100px', height: '100px' }} />;
}

2. Manejo de eventos globales

jsx
import { useEffect } from 'react';

function GlobalEventHandler() {
useEffect(() => {
  const handleResize = () => console.log('Window resized');

  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, []);

return <div>Redimensiona la ventana para ver el efecto.</div>;
}

3. Simulación de carga con useEffect

jsx
import { useEffect, useState } from 'react';

function LoadingSimulation() {
const [loading, setLoading] = useState(true);

useEffect(() => {
  const timer = setTimeout(() => setLoading(false), 2000);
  return () => clearTimeout(timer);
}, []);

return <div>{loading ? 'Cargando...' : 'Carga completa'}</div>;
}

4. Control de scroll con useLayoutEffect

jsx
import { useLayoutEffect, useRef } from 'react';

function ScrollToTop() {
const divRef = useRef(null);

useLayoutEffect(() => {
  divRef.current.scrollTop = 0;
}, []);

return (
  <div ref={divRef} style={{ overflow: 'auto', height: '200px' }}>
    {Array(50)
      .fill('Texto de prueba')
      .map((text, index) => (
        <p key={index}>{text}</p>
      ))}
  </div>
);
}

5. Sincronizar datos con useEffect

jsx
import { useEffect, useState } from 'react';

function SyncData({ data }) {
const [syncedData, setSyncedData] = useState(null);

useEffect(() => {
  setSyncedData(data);
}, [data]);

return <div>Datos sincronizados: {syncedData}</div>;
}
  Conclusión

  • Usa useEffect para operaciones que no necesitan bloquear la pintura del navegador.
  • Usa useLayoutEffect para tareas que dependen de la medición o manipulación del DOM antes del pintado.
  • Comprender cuándo usar cada uno es clave para optimizar el rendimiento y evitar problemas visuales.