Wie React Query 500 Zeilen aus der API-Schicht einer React Native-App entfernte
Ein Fallstudie aus der Praxis zu React Native, die zeigt, wie der Wechsel von manuellem Datenabruf mit useEffect zu React Query wiederholenden Code beseitigte und das Caching sowie die Handhabung im Offline-Modus verbesserte.
Einführung
Vor einiger Zeit stieß eine React Native-Codebasis, die Ihnen vielleicht bekannt vorkommt, auf ein bekanntes Problem.
Nahezu jedes Bildschirm, das auf entfernte Daten angewiesen war, folgte demselben Muster:
- Datenabrufaufrufe innerhalb von
useEffect - Mehrere separate Ladeindikatoren
- Custom-Blöcke zur Fehlerbehandlung
- Eigene Logik für das Ziehen zum Erneuern
- Manuelle Wiederholungsmechanismen
- Sonstige Workarounds für das lokale Caching
Funktional war nichts davon kaputt. Doch die Einhaltung dieser Struktur auf Dutzenden von Bildschirmen wurde zu einer echten Wartungsbelastung.
Durch den Wechsel zu React Query (von TanStack) konnte das Team einen großen Teil des wiederholten Netzwerkcodes entfernen und erhielt im Gegenzug eine bessere Caching-Funktion, eine sauberere Ladeverwaltung sowie ein zuverlässigeres Verhalten im Offline-Modus. Die Bibliothek bietet integriertes Abfragescaching, automatische Hintergrund-Neuabfrage sowie netzwerkorientierte Logik, wodurch der Bedarf, diese Zustandsmechanismen selbst zu programmieren, weitgehend entfällt.
Im Folgenden wird der alte manuelle Ansatz im Vergleich zur React Query-Version dargestellt, wobei Muster aus echten Produktions-React Native-Bildschirmen verwendet werden.
Das Problem mit der traditionellen API-Verwaltung
Die Einrichtung eines typischen Bildschirms sah ungefähr so aus:
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
try {
setLoading(true);
const response = await api.getPosts();
setData(response);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
Dieses gleiche Boilerplate tauchte immer wieder im gesamten Anwendungscode auf.
Jeder Bildschirm musste unabhängig verwalten:
- Ein Ladeflag
- Ein Fehlerflag
- Die Handhabung von Wiederholungsversuchen
Da die App weiter wuchs, wurde dieses wiederkehrende Muster immer schwieriger einheitlich zu halten.
React Query kommt ins Spiel
Derselbe Bildschirm, neu geschrieben mit React Query, reduziert sich auf Folgendes:
const { data, isLoading, error, refetch } = useQuery({
queryKey: ["posts"],
queryFn: fetchPosts,
});
Das ist die gesamte Implementierung.
React Query kümmert sich automatisch um alles Folgende:
- Ladeindikatoren
- Fehlerzustände
- Deduplizierung identischer laufender Anfragen
- Abrufen neuer Daten im Hintergrund
- Verwaltung des Caches
- Erneute Versuche bei fehlgeschlagenen Anfragen
- Reaktion auf Wiederherstellung der Netzverbindung
Der größte Teil dieser Funktionen ist bereits standardmäßig verfügbar und kann über Optionen wie staleTime, gcTime, Konfigurationen für erneute Versuche sowie Einstellungen zum erneuten Abrufen angepasst werden.
Vergleich #1: Netzwerkanfragen
Vor React Query
Stellen Sie sich drei separate Bildschirme vor, die alle dieselben Benutzerprofildaten benötigen.
Ohne eine gemeinsame Caching-Schicht sendet jeder Bildschirm seine eigene Anfrage:
Profile Screen → API Call
Settings Screen → API Call
Dashboard Screen → API Call
Das Ergebnis: Drei separate Netzwerkaufrufe für dieselben Daten.
Mit React Query
Profile Screen → API Call
Settings Screen → Cached Data
Dashboard Screen → Cached Data
Dieses Mal das Ergebnis: Nur ein einziger Netzwerkaufruf.
React Query speichert die Ergebnisse unter einer Abfrage-Identifikation und teilt diese gespeicherten Daten mit allen Komponenten, die danach fragen. Jeder Bildschirm, der später dieselbe Identifikation anfordert, erhält den gespeicherten Wert sofort, während ein Hintergrund-Neuabruf ihn stumm auf dem neuesten Stand hält.
Ergebnis in der Produktion
Auf den von Nutzern häufig besuchten Bildschirmen bedeutet das:
- Weit weniger doppelte Anfragen an die API
- Kleinerer Lastaufwand für die Backend-Server
- Schnellere Übergänge zwischen den Bildschirmen
Vergleich #2: Caching-Effizienz
Caching ist wohl der Bereich, in dem React Query den größten Nutzen bietet.
Wenn derselbe Abfragedaten wieder angefordert wird, bevor die gespeicherte Kopie veraltet:
useQuery({
queryKey: ["products"],
queryFn: getProducts,
staleTime: 300000,
});
Die Benutzeroberfläche kann die gespeicherten Daten sofort anzeigen, wobei React Query sie optional im Hintergrund leise aktualisiert. Gespeicherte Einträge bleiben bestehen und werden schließlich gemäß Ihren Einstellungen gelöscht.
Reales Beispiel
Betrachten Sie den Produktliste-Bildschirm einer E-Commerce-App.
Ohne Caching bedeutet das Öffnen des Bildschirms:
Open Products
↓
Network Request
↓
Navigate Back
↓
Open Products Again
↓
Network Request
Mit React Query sieht derselbe Ablauf so aus:
Open Products
↓
Network Request
↓
Navigate Back
↓
Open Products Again
↓
Instant Cached Data
Der Unterschied ist, dass die App für den Benutzer deutlich schneller wirkt.
Vergleich #3: Ladezustände
Bevor React Query eingeführt wurde, bedeutete das Verfolgen des Ladezustands das Handhaben mehrerer Boolescher Werte:
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [isRetrying, setIsRetrying] = useState(false);
Danach liefert ein einziger Hook-Aufruf alles, was benötigt wird:
const {
isLoading,
isFetching,
isRefetching,
} = useQuery(...)
React Query unterscheidet zwischen dem allerersten Laden und allen nachfolgenden Hintergrundabrufen, wodurch die zugehörige UI-Logik viel einfacher zu verstehen ist.
Tatsächlicher Vorteil
Anstatt bei jedem Abruf einen Vollbild-Loader anzuzeigen, kann die App unterscheiden:
- Erster Laden: Vollbild-Loader
- Hintergrundaktualisierung: Ein kleiner, unauffälliger Loader
- Daten sind bereits im Cache: Keine sichtbare Unterbrechung
Dadurch wirkt die Anwendung für die Nutzer deutlich reaktiver.
Vergleich #4: Offline-Unterstützung
Das Verhalten im Offline-Zustand gehört zu den Dingen, die Teams oft unterschätzen.
Die manuelle Handhabung sieht in der Regel so aus:
Check Connectivity
Pause Requests
Retry Later
Handle Errors
Refetch On Reconnect
Das bedeutet, dass spezielle Verbindungslogiken im gesamten Codebase verteilt sind.
React Query bietet hingegen eine integrierte Verwaltung im Online- und Offline-Modus sowie ein Refetching, das auf Wiederherstellungsereignisse reagiert. In React Native kann man dies mithilfe von onlineManager zusammen mit den Netzwerkstatus-Listenern der Plattform einrichten.
Zum Beispiel:
onlineManager.setEventListener(...)
React Query kann auch so konfiguriert werden, dass es im Offline-First-Modus läuft und sein Netzwerkverhalten entsprechend anpasst.
Echtes Beispiel
Nehmen wir eine Nachrichten-App als Beispiel.
Im Laufe eines Tages könnte die Verbindung des Benutzers wie folgt aussehen:
- Morgen: online
- Nachmittag: offline
- Abend: wieder online
Während dieser gesamten Abfolge bleiben zuvor im Cache gespeicherte Artikel weiterhin lesbar, und sobald die Verbindung wiederhergestellt ist, kann neuer Inhalt automatisch synchronisiert werden.
Echte Anwendungsfälle
1. Nachrichten-Apps
Was das bringt:
- Gecachte Artikel
- Automatische Hintergrundaktualisierung
- Geringerer Gesamt-API-Verkehr
- Möglichkeit, auch offline weiterzulesen
2. E-Commerce-Anwendungen
Vorteile:
- Gecachte Produktlisten
- Vorab-Laden von Kategorien
- Schnellere Navigation zwischen Abschnitten
- Eine deutlich reibungsloseere Einkaufserfahrung
React Query unterstützt außerdem das Vorab-Laden von Daten, noch bevor eine Navigation stattfindet, wodurch die wahrgenommenen Wartezeiten verkürzt werden.
3. Dashboard-Anwendungen
Vorteile:
- Widgets, die sich automatisch aktualisieren
- Ein Cache, der über mehrere Bildschirme geteilt wird
- Geringerer Netzwerkverkehr
- Insgesamt viel einfachereres Zustandsmanagement
Dieses Muster eignet sich besonders gut für Analyse-Dashboards und Admin-Panels.
Was wir tatsächlich entfernt haben
Sobald die Migration abgeschlossen war:
Entfernt
- Eigene Handhabung des Ladezustands
- Manueller Wiederholungscode
- Doppelte Ausgangs-API-Aufrufe
- Standardcode für Pull-to-Refresh
- Eigene Cache-Logik
- Manuelle Verwaltung des Neuladens
Hinzugefügt
- Die React Query-Bibliothek selbst
- Abfrageschlüssel
- Eine
QueryClient-Instanz
Das Ergebnis: Etwa 500 Zeilen an Code zur API-Verarbeitung waren nicht mehr erforderlich.
Wann React Query möglicherweise nicht notwendig ist
Die Verwendung von React Query könnte übertrieben sein, wenn:
- Ihre Anwendung nur wenige API-Aufrufe macht
- Die zugrunde liegenden Daten kaum ändern
- Echtzeit-Caching tatsächlich nicht benötigt wird
- Das Verhalten im Offline-Modus für Ihren Anwendungsfall nicht wichtig ist
Doch bei den meisten Produktionsanwendungen überwiegen die Vorteile recht schnell die anfängliche Lernkurve.
Fazit
React Query ist mehr als nur eine weitere Möglichkeit, Daten abzurufen.
Es fungiert als vollständige Lösung für die Verwaltung des Server-Zustands, indem es wiederholenden API-Code eliminiert und gleichzeitig das Caching, das Ladenverhalten, die Netzwerkeffizienz sowie die Nutzung im Offline-Modus verbessert.
Der größte Vorteil lag hier nicht im reinen Leistungswert.
Sonderlich die Verringerung der Komplexität.
Genau diese Kombination machte den Umstieg lohnenswert.