useEffect Avanzado y Patrones
Volver a clases
●●Intermedio

useEffect Avanzado y Patrones

120 min
0 vistas

Race conditions, debouncing, polling y organización de efectos.

Race conditions y abort

  • Cuando las respuestas llegan en distinto orden, el estado queda desfasado.
  • Se soluciona con AbortController o flags de cancelación.

Debouncing

  • Evita solicitudes en cada tecla y reduce carga en el servidor.
  • Se implementa con un hook que retrasa el valor.

Polling y refetch

  • Actualiza datos cada cierto tiempo o bajo demanda.
  • Útil para dashboards o notificaciones.

Separación de efectos

  • Un efecto por responsabilidad mejora el mantenimiento.
  • Ayuda a manejar dependencias sin errores.

Dependencias, cierres y estado desactualizado

  • El efecto captura el estado/props del render en el que se definio.
  • Si usas valores viejos dentro del efecto, aparece el problema de "stale closure".
  • Solucion: agrega dependencias correctas o usa useRef para guardar valores mutables.

Async en useEffect

  • No declares useEffect como async directamente.
  • Define una funcion interna async y manejala con try/catch.
  • Usa AbortController o un flag ignore para evitar actualizar estado en un efecto cancelado.

Evitar loops de render

  • Si un efecto actualiza un estado que es dependencia directa, puede generar loops.
  • Verifica si la actualizacion es realmente necesaria antes de llamar a setState.
  • Usa condiciones o compara valores previos.

Integrar eventos del DOM

  • Registra listeners en montaje y limpia en desmontaje.
  • Los handlers deben ser estables para evitar duplicados.

Cuándo crear un custom hook

  • Si un efecto se repite en varios componentes, extraelo a un hook.
  • Encapsula el estado, el efecto y la interfaz publica del comportamiento.

Ejemplos de Código

5 ejemplos

Cancelación de requests

javascript
1import { useEffect, useState } from "react";
2
3function Buscar({ query }) {
4  const [data, setData] = useState([]);
5
6  useEffect(() => {
7    const controller = new AbortController();
8
9    fetch(`/api/search?q=${query}`, { signal: controller.signal })
10      .then((res) => res.json())
11      .then((json) => setData(json))
12      .catch((error) => {
13        if (error.name !== "AbortError") console.error(error);
14      });
15
16    return () => controller.abort();
17  }, [query]);
18
19  return <ul>{data.map((item) => <li key={item.id}>{item.name}</li>)}</ul>;
20}

Debounce reutilizable

javascript
1import { useEffect, useState } from "react";
2
3function useDebounce(value, delay) {
4  const [debounced, setDebounced] = useState(value);
5
6  useEffect(() => {
7    const id = setTimeout(() => setDebounced(value), delay);
8    return () => clearTimeout(id);
9  }, [value, delay]);
10
11  return debounced;
12}

Polling con visibilidad

javascript
1import { useEffect, useState } from "react";
2
3function Stats() {
4  const [stats, setStats] = useState(null);
5  const [visible, setVisible] = useState(!document.hidden);
6
7  useEffect(() => {
8    const onVisibility = () => setVisible(!document.hidden);
9    document.addEventListener("visibilitychange", onVisibility);
10    return () => document.removeEventListener("visibilitychange", onVisibility);
11  }, []);
12
13  useEffect(() => {
14    if (!visible) return;
15
16    const cargar = async () => {
17      const res = await fetch("/api/stats");
18      setStats(await res.json());
19    };
20
21    cargar();
22    const id = setInterval(cargar, 5000);
23    return () => clearInterval(id);
24  }, [visible]);
25
26  return <pre>{JSON.stringify(stats, null, 2)}</pre>;
27}

Ref para valores actuales

javascript
1import { useEffect, useRef } from "react";
2
3function Logger({ value }) {
4  const latest = useRef(value);
5
6  useEffect(() => {
7    latest.current = value;
8  }, [value]);
9
10  useEffect(() => {
11    const id = setInterval(() => {
12      console.log("Ultimo valor:", latest.current);
13    }, 1000);
14    return () => clearInterval(id);
15  }, []);
16
17  return null;
18}

Async con cancelacion

javascript
1import { useEffect, useState } from "react";
2
3function Detalle({ id }) {
4  const [data, setData] = useState(null);
5
6  useEffect(() => {
7    let ignore = false;
8
9    const cargar = async () => {
10      const res = await fetch(`/api/items/${id}`);
11      const json = await res.json();
12      if (!ignore) setData(json);
13    };
14
15    if (id) cargar();
16    return () => {
17      ignore = true;
18    };
19  }, [id]);
20
21  return <pre>{JSON.stringify(data, null, 2)}</pre>;
22}

Recursos

3 recursos disponibles

¡Hora de Practicar!

PrácticaPrincipiante15 min

Práctica guiada - Buscador con debounce

Práctica

Input de búsqueda que dispara requests con debounce y cancelación de peticiones.

Desafío de Código

EjercicioPrincipiante15 min

Ejercicios - Patrones con efectos

Ejercicios

(1) Refetch manual con botón, (2) polling configurable, (3) separar efectos por responsabilidad.

Documentación Oficial

DocumentaciónPrincipiante15 min

A Complete Guide to useEffect

Guía avanzada para entender dependencias y cierres.

ALVESC ACADEMY - Plataforma Educativa