certmundo.
es‑mx

6 min de lectura

¿Cómo manejar efectos secundarios con useEffect?

useEffect es el hook de React que ejecuta código fuera del flujo de renderizado, como llamadas a una API, temporizadores o suscripciones.

Cada vez que React pinta un componente en pantalla, useEffect se ejecuta después. Esto lo hace ideal para operaciones que no forman parte del render visual.


¿Qué es un efecto secundario?

Un efecto secundario es cualquier operación que afecta algo fuera del componente. Ejemplos comunes:

  • Consultar una API de productos de Mercado Libre
  • Iniciar un temporizador con setInterval
  • Guardar datos en localStorage
  • Suscribirse a un WebSocket

React separa el renderizado (pintar la UI) de los efectos (todo lo demás). useEffect es el puente entre los dos.


Sintaxis de useEffect

useEffect(función, [dependencias]);

Tiene dos partes obligatorias:

Parte Descripción
función El código que quieres ejecutar como efecto
[dependencias] Arreglo que controla cuándo se vuelve a ejecutar el efecto

El arreglo de dependencias es lo más importante de useEffect. Controla completamente el ciclo de vida del efecto.


Tres modos de ejecución

El arreglo de dependencias define cuándo corre tu efecto. Hay tres casos:

1. Sin arreglo — se ejecuta en cada render

useEffect(() => {
  console.log("El componente se renderizó");
});

Esto corre después de cada actualización del componente. Rara vez es lo que necesitas.

2. Arreglo vacío [] — se ejecuta solo al montar

useEffect(() => {
  console.log("Solo al montar el componente");
}, []);

Este es el equivalente de componentDidMount en clases. Úsalo para cargar datos iniciales.

3. Arreglo con dependencias — se ejecuta cuando cambia una variable

useEffect(() => {
  console.log("El valor cambió:", busqueda);
}, [busqueda]);

El efecto corre solo cuando busqueda cambia. Perfecto para búsquedas en tiempo real.


Ejemplo 1 — Cargar productos de una API al montar

Imagina que construyes una tienda similar a Liverpool. Al cargar la página, necesitas traer los productos del servidor.

import { useState, useEffect } from "react";

function CatalogoProductos() {
  const [productos, setProductos] = useState([]);
  const [cargando, setCargando] = useState(true);

  useEffect(() => {
    fetch("https://api.liverpool.com.mx/productos")
      .then((res) => res.json())
      .then((data) => {
        setProductos(data);
        setCargando(false);
      });
  }, []);

  if (cargando) return <p>Cargando productos...</p>;

  return (
    <ul>
      {productos.map((p) => (
        <li key={p.id}>{p.nombre} — ${p.precio.toLocaleString("es-MX")}</li>
      ))}
    </ul>
  );
}

El arreglo vacío [] garantiza que la llamada al API ocurre una sola vez. Sin él, el componente haría la llamada en bucle infinito.


Ejemplo 2 — Filtrar productos según búsqueda

Ahora el usuario puede escribir en un campo de búsqueda. Cada vez que cambia el texto, quieres consultar el API de nuevo.

function BuscadorFEMSA() {
  const [termino, setTermino] = useState("");
  const [resultados, setResultados] = useState([]);

  useEffect(() => {
    if (termino === "") {
      setResultados([]);
      return;
    }

    fetch(`https://api.femsa.com/buscar?q=${termino}`)
      .then((res) => res.json())
      .then((data) => setResultados(data));
  }, [termino]);

  return (
    <div>
      <input
        value={termino}
        onChange={(e) => setTermino(e.target.value)}
        placeholder="Buscar producto FEMSA..."
      />
      <ul>
        {resultados.map((r) => (
          <li key={r.id}>{r.nombre}</li>
        ))}
      </ul>
    </div>
  );
}

Aquí [termino] le dice a React: "ejecuta este efecto cada vez que termino cambie". Si el usuario borra el campo, el efecto limpia los resultados y sale con return.


La función de limpieza (cleanup)

Algunos efectos deben detenerse cuando el componente se desmonta. Por ejemplo, un temporizador que sigue corriendo aunque el usuario ya cambió de página.

Para eso, useEffect acepta una función de retorno que React llama al limpiar el efecto.

useEffect(() => {
  const intervalo = setInterval(() => {
    console.log("Verificando stock de Bimbo...");
  }, 5000);

  return () => {
    clearInterval(intervalo);
    console.log("Temporizador detenido");
  };
}, []);

Cuando el componente desaparece de la pantalla, React ejecuta el return. El clearInterval detiene el temporizador. Sin esto, el intervalo seguiría corriendo en memoria.


Ejemplo 3 — Temporizador con limpieza en un dashboard

Imagina un dashboard de ventas de Mercado Libre que actualiza el total cada 10 segundos.

function DashboardVentas() {
  const [totalVentas, setTotalVentas] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      fetch("https://api.mercadolibre.com/ventas/total")
        .then((res) => res.json())
        .then((data) => setTotalVentas(data.total));
    }, 10000);

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

  return (
    <div>
      <h2>Ventas del día</h2>
      <p>Total: ${totalVentas.toLocaleString("es-MX")}</p>
    </div>
  );
}

El arreglo vacío significa que el intervalo se crea solo una vez. La función de limpieza lo destruye cuando el componente se desmonta.


Errores comunes

Error 1 — Olvidar el arreglo de dependencias

// ❌ Incorrecto: corre en cada render
useEffect(() => {
  fetch("/api/productos").then(...);
});

// ✅ Correcto: corre solo al montar
useEffect(() => {
  fetch("/api/productos").then(...);
}, []);

Sin el arreglo, el efecto llama al API en cada renderizado. Esto puede generar cientos de llamadas innecesarias.

Error 2 — Bucle infinito por dependencia que cambia dentro del efecto

// ❌ Bucle infinito
const [datos, setDatos] = useState([]);

useEffect(() => {
  fetch("/api/datos")
    .then((res) => res.json())
    .then((data) => setDatos(data)); // cambia 'datos'
}, [datos]); // pero 'datos' está en las dependencias

Cada vez que setDatos corre, datos cambia. Eso dispara el efecto de nuevo. Eso vuelve a cambiar datos. Y así en bucle infinito.

Solución: Quita datos del arreglo si no lo necesitas como disparador.

// ✅ Correcto
useEffect(() => {
  fetch("/api/datos")
    .then((res) => res.json())
    .then((data) => setDatos(data));
}, []); // sin 'datos' en dependencias

Error 3 — No limpiar efectos con temporizadores o suscripciones

// ❌ El intervalo nunca se detiene
useEffect(() => {
  setInterval(() => console.log("tick"), 1000);
}, []);

// ✅ Se limpia al desmontar
useEffect(() => {
  const id = setInterval(() => console.log("tick"), 1000);
  return () => clearInterval(id);
}, []);

Sin la función de limpieza, el intervalo sigue corriendo aunque el componente ya no exista. Esto causa fugas de memoria.


Tabla resumen: arreglo de dependencias

Arreglo ¿Cuándo corre el efecto? Caso de uso típico
Sin arreglo En cada render Depuración, logging
[] vacío Solo al montar Cargar datos iniciales
[var1, var2] Cuando var1 o var2 cambian Búsqueda reactiva, filtros

Comparación: con y sin limpieza

Efecto ¿Necesita limpieza? Ejemplo
fetch a una API No siempre Cargar catálogo de productos
setInterval Dashboard de ventas
addEventListener Detectar scroll o teclas
setTimeout Sí si es largo Notificación automática

Puntos clave para recordar

  • useEffect se ejecuta después de que React pinta el componente, nunca durante.
  • El arreglo de dependencias controla cuándo se repite el efecto. Nunca lo omitas sin pensar.
  • Un arreglo vacío [] equivale a "ejecutar solo al montar". Es el caso más común para cargar datos.
  • La función de retorno dentro de useEffect limpia el efecto anterior. Úsala con temporizadores y suscripciones.
  • Si ves un bucle infinito, revisa si alguna variable que seteas dentro del efecto también está en las dependencias.

Puntos clave

  • `useEffect(fn, [])` con arreglo vacío ejecuta el efecto solo al montar el componente. Es el patrón correcto para cargar datos iniciales desde una API.
  • El arreglo de dependencias `[variable]` le dice a React que repita el efecto solo cuando esa variable cambia. Omitirlo por completo causa que el efecto corra en cada render.
  • Si dentro del efecto llamas a `setState` con una variable que también está en las dependencias, crearás un bucle infinito. Revisa siempre qué variables pones en el arreglo.
  • La función de retorno de `useEffect` es la función de limpieza. Úsala para detener `setInterval`, cancelar `setTimeout` o remover `addEventListener` cuando el componente se desmonte.
  • Un efecto sin arreglo de dependencias es válido, pero raro. Úsalo solo para depuración o logging, nunca para llamadas a APIs o temporizadores.

Comparte esta lección: