Volver a React Básico
useEffect y Efectos Secundarios
useEffect ejecuta código después de que el componente renderiza — útil para obtener datos, suscripciones e interacciones con el DOM.
useEffect Básico
import { useState, useEffect } from 'react';
function Temporizador() {
const [segundos, setSegundos] = useState(0);
useEffect(() => {
// Se ejecuta después de cada renderizado (sin array de dependencias)
document.title = `Temporizador: ${segundos}s`;
});
useEffect(() => {
// Se ejecuta una vez al montar (array de dependencias vacío)
console.log("¡Componente montado!");
}, []);
useEffect(() => {
// Se ejecuta cuando `segundos` cambia
console.log("Segundos cambiaron:", segundos);
}, [segundos]);
return <p>{segundos} segundos</p>;
}
Función de Limpieza
function TemporizadorVivo() {
const [hora, setHora] = useState(new Date());
useEffect(() => {
const intervalo = setInterval(() => {
setHora(new Date());
}, 1000);
// Limpieza: se ejecuta antes del próximo efecto y al desmontar
return () => clearInterval(intervalo);
}, []); // Solo se configura una vez
return <p>{hora.toLocaleTimeString()}</p>;
}
Patrón para Obtener Datos
interface Usuario {
id: number;
nombre: string;
correo: string;
}
function PerfilUsuario({ idUsuario }: { idUsuario: number }) {
const [usuario, setUsuario] = useState<Usuario | null>(null);
const [cargando, setCargando] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelado = false; // Evitar actualizar estado en componente desmontado
async function cargarUsuario() {
try {
setCargando(true);
setError(null);
const response = await fetch(`/api/usuarios/${idUsuario}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const datos = await response.json();
if (!cancelado) setUsuario(datos);
} catch (err) {
if (!cancelado) setError(err instanceof Error ? err.message : "Error desconocido");
} finally {
if (!cancelado) setCargando(false);
}
}
cargarUsuario();
return () => { cancelado = true; };
}, [idUsuario]); // Se vuelve a ejecutar cuando idUsuario cambia
if (cargando) return <div className="spinner" />;
if (error) return <p className="error">{error}</p>;
if (!usuario) return null;
return (
<div>
<h2>{usuario.nombre}</h2>
<p>{usuario.correo}</p>
</div>
);
}
useRef
import { useRef, useEffect } from 'react';
function InputAutoEnfoque() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// Enfocar al montar
inputRef.current?.focus();
}, []);
return <input ref={inputRef} placeholder="Auto-enfocado" />;
}
// Almacenar valor mutable sin disparar re-renderizado
function CronometroConRef() {
const [corriendo, setCorriendo] = useState(false);
const intervaloRef = useRef<number>(0);
const iniciar = () => {
setCorriendo(true);
intervaloRef.current = window.setInterval(() => { /* ... */ }, 100);
};
const detener = () => {
setCorriendo(false);
clearInterval(intervaloRef.current);
};
}
Error común: Añadir
asyncdirectamente al callback deuseEffect. En cambio, define una función async dentro y llámala inmediatamente.