Kodokon kodokon.com

Eine API aufrufen: fetch, Laden und Fehler

Lade entfernte Daten mit fetch innerhalb eines Effekts und modelliere die Lade- und Fehlerzustände sauber.

10 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Eine Netzwerkanfrage braucht Zeit und kann fehlschlagen: Deine Oberfläche muss diese Realitäten widerspiegeln. Die professionelle Konvention ist, drei verschiedene State-Teile zu verfolgen: die empfangenen Daten, einen loading-Boolean und einen möglichen Fehler. Die Anfrage kommt in ein useEffect, weil das Kontaktieren eines Servers ein Seiteneffekt ist.

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));
}, []);
Drei Zustände für eine Anfrage: data, loading, error.

Behandle die Fälle im Rendering der Reihe nach: zuerst das Laden, dann den Fehler und schließlich die Daten. Diese frühen Returns halten den Code lesbar und garantieren, dass der Nutzer immer etwas Kohärentes sieht: eine Warteanzeige, eine Fehlermeldung oder die erwartete Liste.

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>
);
Frühe Returns: Es wird immer nur ein Fall gleichzeitig angezeigt.

Eine letzte Falle: Wenn sich eine Abhängigkeit ändert (zum Beispiel eine userId), geht eine neue Anfrage raus, während die alte vielleicht noch unterwegs ist. Wenn die alte zuletzt antwortet, würde sie die frischen Daten überschreiben. Die klassische Gegenmaßnahme: ein ignore-Flag, das von der Cleanup-Funktion auf true gesetzt wird, um veraltete Antworten wegzuwerfen.

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]);
Jede veraltete Antwort wird dank des Flags verworfen.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Warum drei Zustände (data, loading, error) für eine Anfrage verfolgen?
    • Weil fetch diese drei Variablen zum Funktionieren benötigt
    • Damit die Oberfläche jede Phase widerspiegelt: Warten, Fehlschlag oder Erfolg
    • Um zu verhindern, dass React die Anfrage bei jedem Rendern erneut auslöst
  2. Deine API antwortet mit einem 500-Status, aber der catch wird nie ausgelöst. Warum?
    • fetch lehnt das Promise nur bei einem Netzwerkfehler ab, nicht bei einem HTTP-Fehlerstatus
    • catch funktioniert nicht innerhalb eines useEffect
    • Der Server muss einen speziellen Header senden, um den catch auszulösen
  3. Wofür ist das ignore-Flag da, das im Cleanup des Effekts auf true gesetzt wird?
    • Um die laufende HTTP-Anfrage auf der Serverseite abzubrechen
    • Um zu verhindern, dass React den Effekt erneut ausführt
    • Um die Antwort einer Anfrage zu verwerfen, die veraltet ist