Kodokon kodokon.com

Llamar a una API: fetch, carga y errores

Carga datos remotos con fetch dentro de un efecto, modelando de forma limpia los estados de carga y de error.

10 min · 3 preguntas

Abrir esta lección en Kodokon

Una petición de red lleva tiempo y puede fallar: tu interfaz debe reflejar esas realidades. La convención profesional es rastrear tres porciones de estado distintas: los datos recibidos, un booleano de carga y un posible error. La petición va dentro de un useEffect, porque contactar con un servidor es un efecto secundario.

JSX
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  fetch("https://api.example.com/users")
    .then((res) => {
      if (!res.ok) throw new Error("HTTP " + res.status);
      return res.json();
    })
    .then((data) => setUsers(data))
    .catch((err) => setError(err.message))
    .finally(() => setLoading(false));
}, []);
Tres estados para una petición: datos, carga, error.

En el renderizado, gestiona los casos en orden: primero la carga, luego el error y, por último, los datos. Estos retornos tempranos mantienen el código legible y garantizan que el usuario siempre vea algo coherente: un indicador de espera, un mensaje de fallo o la lista esperada.

JSX
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error}</p>;

return (
  <ul>
    {users.map((user) => (
      <li key={user.id}>{user.name}</li>
    ))}
  </ul>
);
Retornos tempranos: solo un caso mostrado a la vez.

Una última trampa: si una dependencia cambia (un userId, por ejemplo), sale una nueva petición mientras la antigua puede seguir en curso. Si la antigua responde en último lugar, sobrescribiría los datos recientes. La contramedida clásica: un indicador ignore puesto a true por la función de limpieza, para descartar las respuestas obsoletas.

JSX
useEffect(() => {
  let ignore = false;
  setLoading(true);

  fetch("/api/users/" + userId)
    .then((res) => res.json())
    .then((data) => {
      if (!ignore) setUser(data);
    })
    .finally(() => {
      if (!ignore) setLoading(false);
    });

  return () => {
    ignore = true;
  };
}, [userId]);
Toda respuesta obsoleta se descarta gracias al indicador.

Prueba de conocimientos

Comprueba que has retenido los puntos clave de esta lección.

  1. ¿Por qué rastrear tres estados (datos, carga, error) para una petición?
    • Porque fetch requiere estas tres variables para funcionar
    • Para que la interfaz refleje cada fase: espera, fallo o éxito
    • Para evitar que React vuelva a lanzar la petición en cada renderizado
  2. Tu API responde con un estado 500, pero el catch nunca se dispara. ¿Por qué?
    • fetch solo rechaza la promesa ante un fallo de red, no ante un estado de error HTTP
    • catch no funciona dentro de un useEffect
    • El servidor debe enviar una cabecera especial para desencadenar el catch
  3. ¿Para qué sirve el indicador ignore, puesto a true en la limpieza del efecto?
    • Para cancelar la petición HTTP en curso del lado del servidor
    • Para evitar que React vuelva a ejecutar el efecto
    • Para descartar la respuesta de una petición que ha quedado obsoleta