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 async directamente al callback de useEffect. En cambio, define una función async dentro y llámala inmediatamente.