DevPath · Aprende a programar ESPTEN

Datos y estado global

Data fetching moderno: TanStack Query y SWR

El problema de useEffect + fetch a mano

Cargar datos del servidor parece sencillo: un useEffect que hace fetch y guarda el resultado en estado. Es lo primero que aprendemos, y para un caso aislado funciona:

function Perfil({ userId }) {
  const [usuario, setUsuario] = useState(null);
  const [cargando, setCargando] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelado = false;
    setCargando(true);
    fetch(`/api/usuarios/${userId}`)
      .then((r) => {
        if (!r.ok) throw new Error("Error " + r.status);
        return r.json();
      })
      .then((data) => { if (!cancelado) setUsuario(data); })
      .catch((e) => { if (!cancelado) setError(e); })
      .finally(() => { if (!cancelado) setCargando(false); });
    return () => { cancelado = true; };
  }, [userId]);

  // ...renderizado según cargando / error / usuario
}

Mira cuánto código de fontanería hay para una sola petición, y eso que aún le faltan cosas. Cuando la app crece, este patrón se queda corto porque cada pantalla tiene que reinventar:

Estos problemas no son del estado del cliente (lo que el usuario teclea o selecciona): son del estado del servidor, datos que viven en remoto, los cacheamos en el cliente y pueden quedar desactualizados. Las librerías de data fetching nacen para gestionar exactamente eso.

TanStack Query (React Query)

TanStack Query trata el estado del servidor como un recurso con caché. La pieza central es el hook useQuery:

import { useQuery } from "@tanstack/react-query";

function Perfil({ userId }) {
  const { data, isLoading, error } = useQuery({
    queryKey: ["usuario", userId],
    queryFn: () =>
      fetch(`/api/usuarios/${userId}`).then((r) => r.json()),
    staleTime: 60_000, // datos "frescos" durante 60 s
  });

  if (isLoading) return <p>Cargando…</p>;
  if (error) return <p>Algo salió mal</p>;
  return <h1>{data.nombre}</h1>;
}

Las claves:

Todo lo que antes escribías a mano —caché, deduplicación, revalidación, cancelación— viene de fábrica.

SWR

SWR (de Vercel) sigue la misma filosofía con una API más minimalista. Su nombre viene de stale-while-revalidate: muestra los datos cacheados (stale) al instante y, en paralelo, revalida en segundo plano.

import useSWR from "swr";

const fetcher = (url) => fetch(url).then((r) => r.json());

function Perfil({ userId }) {
  const { data, error, isLoading } = useSWR(
    `/api/usuarios/${userId}`,
    fetcher
  );

  if (isLoading) return <p>Cargando…</p>;
  if (error) return <p>Algo salió mal</p>;
  return <h1>{data.nombre}</h1>;
}

Aquí la clave de caché es la propia URL (primer argumento) y el fetcher es la función de descarga. SWR también deduplica peticiones, revalida al reenfocar y reconectar, y expone mutate() para actualizar la caché.

Idea clave: separa el estado del servidor (datos remotos cacheados → TanStack Query / SWR) del estado del cliente (UI local → useState, Zustand...). No metas datos del servidor en un store global "a mano"; deja que una librería de queries gestione su caché y su frescura.

(Estas librerías se instalan aparte; aquí las estudiamos de forma conceptual. En los ejercicios validables seguimos usando los hooks nativos de React.)

Pon esto en práctica

DevPath es un curso práctico: aquí lees la teoría; en la app la pones en práctica con ejercicios que se ejecutan de verdad, sin conexión.

Empezar gratis en la app →
Estado global: más allá de Context →