Busca en todas las páginas de la documentación
Sincroniza un componente con un sistema externo ejecutando efectos secundarios después de la renderización.
Tarjeta de referencia rápida — lista para copiar y pegar.
// Ejecutar después de cada renderización
useEffect(() => {
console.log("renderizado");
});
// Ejecutar una sola vez al montar
useEffect(() => {
fetchData();
}, []);
// Ejecutar cuando las dependencias cambian
useEffect(() => {
document.title = `Hiciste clic ${count} veces`;
}, [count]);
// Limpiar al desmontar o antes de reintentar
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);Cuándo recurrir a esto: Necesitas sincronizar con algo fuera de React — obtener datos, suscribirse a eventos, manipular el DOM o configurar temporizadores.
"use client";
import { useEffect, useState } from "react";
export function WindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
function
Lo que esto demuestra:
resize) dentro de useEffect[] para que el efecto se ejecute solo una vez al montaruseEffect se ejecuta después de que el navegador pinte la pantalla, no durante la renderización| Parámetro | Tipo | Descripción |
|---|---|---|
setup | () => void o () => () => void | Función de efecto; opcionalmente devuelve una función de limpieza |
dependencies | unknown[] u omitido | Array de valores reactivos de los que depende el efecto |
| Retorno | Tipo | Descripción |
|---|---|---|
| (ninguno) | void | useEffect no devuelve un valor |
Obtención de datos con limpieza (controlador de aborto):
useEffect(() => {
const controller = new AbortController();
async function load() {
const res = await fetch(`/api/users/${id}`, {
signal: controller.signal,
});
const data =
Conexión a un WebSocket:
useEffect(() => {
const ws = new WebSocket(url);
ws.onmessage = (e) => setMessages((prev) => [...prev, JSON.parse(e.data)]);
return () =>
Sincronizar con localStorage:
useEffect(() => {
localStorage.setItem("theme", theme);
}, [theme]);// Efecto con async — debe envolver porque useEffect no puede devolver una Promise
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
const json: DataType = await res.json();
setData(json);
}
Bucle infinito — Actualizar estado que también es una dependencia causa que el efecto se reintenté sin fin. Solución: Reduce tu array de dependencias o mueve la actualización de estado detrás de una condición.
Dependencias faltantes — Omitir un valor del array de dependencias causa datos obsoletos dentro del efecto. Solución: Incluye todos los valores reactivos usados dentro del efecto, o refactoriza para remover la dependencia.
Async directamente en useEffect — Devolver una Promise en lugar de una función de limpieza produce una advertencia de React. Solución: Define una función async dentro del efecto y llámala inmediatamente.
Condiciones de carrera en rerenderizaciones rápidas — Si id cambia rápidamente, un fetch anterior puede resolverse después de uno más nuevo y sobrescribir datos correctos. Solución: Usa un AbortController o una bandera booleana ignore en la limpieza.
Ejecutar en el servidor — useEffect no se ejecuta durante SSR. Acceder a window o document sin guardar causa errores de compilación. Solución: Accede a APIs del navegador solo dentro de useEffect o detrás de un chequeo typeof window !== "undefined".
| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
useLayoutEffect | Necesitas medir o mutar el DOM antes de que el navegador pinte | El efecto no toca el layout o el DOM visual |
| Manejador de eventos | El efecto secundario es disparado por una acción específica del usuario (clic, envío) | El efecto debe ejecutarse al montar o cuando un valor cambia |
fetch de Server Component | Los datos pueden obtenerse en tiempo de solicitud en el servidor | Los datos dependen del estado del lado del cliente o interacción del usuario |
useSyncExternalStore | Suscribirse a una tienda externa (Redux, API del navegador) | Configuración única u obtención de datos |
¿Por qué no obtener datos en useEffect? Para muchas aplicaciones, marcos como Next.js ofrecen obtención de datos del lado del servidor que evita spinners de carga y cascadas. Reserva la obtención de datos en useEffect para necesidades verdaderamente solo del cliente.
De una aplicación SaaS de producción Next.js 15 / React 19 (SystemsArchitect.io).
// Ejemplo de producción: Navegación por teclado para tarjetas de estudio
// Archivo: src/hooks/use-card-keyboard.ts
import { useEffect } from "react";
interface UseCardKeyboardOptions {
isAutoplayOn: boolean;
cardNumber: number;
totalCards: number;
hasMoreCards: boolean;
onCardClick
Lo que esto demuestra en producción:
keydown al desmontar y cuando las dependencias cambian, previniendo que se acumulen manejadores obsoletos.cardNumber) causaría bugs de cierre obsoleto donde se capturan valores antiguos.onCardClick, onNext y onPrev deben envolverse en useCallback por el componente padre. Sin eso, cada rerenderización padre crea nuevas referencias de función, causando que este efecto se desuscriba y se resuscriba el listener innecesariamente.isAutoplayOn en la parte superior del manejador es un patrón de producción para deshabilitar la entrada de teclado durante la reproducción automatizada sin remover completamente el listener.[] y ningún array de dependencias?useEffect(() => { ... }, []) se ejecuta una sola vez al montar y se limpia al desmontar.useEffect(() => { ... }) se ejecuta después de cada renderización.useEffect espera que el callback devuelva undefined o una función de limpieza.async devuelve una Promise, que React no puede usar como función de limpieza.useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
setData(await res.json());
}
AbortController para cancelar la solicitud anterior cuando cambian las dependencias.ignore establecida en true en la función de limpieza.useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${id}`, { signal: controller.signal })
.then(res => res.json())
.
useEffect cuando necesitas sincronizar con un sistema externo al montar o cuando una dependencia cambia.Object.is, que verifica igualdad de referencia.{} o array [] creado durante la renderización tiene una nueva referencia cada vez.useMemo o extrayendo dependencias primitivas.useEffect((): (() => void) => {
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
// TypeScript infiere el tipo de limpieza automáticamente
// en la mayoría de casos — la tipificación explícita rara vez es necesaria.useEffect solo se ejecuta en el cliente, por lo que window y document siempre están disponibles dentro de él.useEffect durante renderización del lado del servidor.useEffect o detrás de una guardia typeof window !== "undefined".useEffect se ejecuta después de que el navegador pinta la pantalla (asincrónico).useLayoutEffect se ejecuta sincrónico después de mutaciones del DOM pero antes de que el navegador pinte.useLayoutEffect solo cuando necesites medir o mutar el DOM antes de que el usuario lo vea (p. ej., tooltips, animaciones).