TanStack Query für intelligentere Datenabrufe in React verwenden

Wenn Sie React-Apps entwickelt haben, die mit APIs kommunizieren, kennen Sie das Muster: useEffect
für den Abruf, useState
für die Daten, ein weiteres useState
für das Laden, vielleicht noch eines für Fehler. Bevor Sie es merken, verwalten Sie ein verworrenes Durcheinander von State, nur um eine Liste von Benutzern anzuzeigen.
Dieser manuelle Ansatz funktioniert, ist aber fragil. Was passiert, wenn der Benutzer wegnavigiert und zurückkommt? Rufen Sie erneut ab? Verwenden Sie veraltete Daten? Was ist mit Wiederholungsversuchen, wenn das Netzwerk ausfällt? Das sind keine Grenzfälle – das ist die Realität von Produktions-Apps.
TanStack Query (früher React Query) löst diese Probleme, indem es Server-State anders als Client-State behandelt. Anstatt imperativ Daten abzurufen, deklarieren Sie, was Sie benötigen, und lassen die Bibliothek Caching, Synchronisation und Updates handhaben. Dieser Artikel zeigt Ihnen, wie Sie von manuellem Datenabruf zu einem intelligenteren, deklarativen Ansatz wechseln, der skaliert.
Wichtige Erkenntnisse
- Ersetzen Sie manuelle
useEffect
+useState
-Muster durch deklarativeuseQuery
-Hooks für saubereren, wartbareren Code - Nutzen Sie automatisches Caching, Hintergrund-Refetching und Request-Deduplizierung zur Verbesserung der Benutzererfahrung
- Verwenden Sie Mutationen mit optimistischen Updates, um responsive UIs zu erstellen, die sich sofort anfühlen
- Implementieren Sie geeignete Query-Invalidierungsstrategien, um Daten in Ihrer Anwendung synchron zu halten
- Vermeiden Sie häufige Fallstricke wie die Verwendung von TanStack Query für lokalen State oder das Vergessen dynamischer Parameter in Query Keys
Das Problem mit manuellem Datenabruf
Hier ist eine typische React-Komponente, die Daten abruft:
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUsers() {
setIsLoading(true);
setError(null);
try {
const response = await fetch('/api/users');
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
if (!cancelled) {
setUsers(data);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
}
} finally {
if (!cancelled) {
setIsLoading(false);
}
}
}
fetchUsers();
return () => { cancelled = true };
}, []);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Das funktioniert, aber schauen Sie, was wir verwalten:
- Drei separate State-Teile
- Cleanup-Logik zur Verhinderung von State-Updates bei unmountierten Komponenten
- Kein Caching – jedes Mount löst einen neuen Abruf aus
- Keine Retry-Logik für fehlgeschlagene Requests
- Keine Möglichkeit zu wissen, ob unsere Daten veraltet sind
Multiplizieren Sie diese Komplexität mit Dutzenden von Komponenten. Hier kommt TanStack Query ins Spiel.
Wie TanStack Query Datenabruf intelligenter macht
TanStack Query behandelt Server-State als erstklassigen Bürger. Anstatt manuell Abrufe zu orchestrieren, beschreiben Sie Ihre Datenanforderungen deklarativ:
import { useQuery } from '@tanstack/react-query';
function UserList() {
const { data: users, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => {
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}),
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{users?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Gleiche Funktionalität, aber beachten Sie, was fehlt:
- Kein
useState
für Daten, Laden oder Fehler - Kein
useEffect
oder Cleanup-Logik - Keine manuelle State-Synchronisation
Aber hier ist, was Sie gewinnen:
- Automatisches Caching: Navigieren Sie weg und zurück, sehen Sie sofortige Ergebnisse
- Hintergrund-Refetching: Veraltete Daten werden stillschweigend aktualisiert
- Request-Deduplizierung: Mehrere Komponenten können dieselbe Query teilen
- Eingebaute Retry-Logik: Fehlgeschlagene Requests werden automatisch wiederholt
- Optimistische Updates: UI-Updates vor Server-Bestätigung
Kernkonzepte für intelligenteres Datenmanagement
Queries: Server-State lesen
Queries rufen Daten ab und cachen sie. Der useQuery
-Hook akzeptiert ein Konfigurationsobjekt mit zwei wesentlichen Eigenschaften:
const { data, isLoading, error } = useQuery({
queryKey: ['todos', userId, { status: 'active' }],
queryFn: () => fetchTodosByUser(userId, { status: 'active' }),
staleTime: 5 * 60 * 1000, // 5 Minuten
gcTime: 10 * 60 * 1000, // 10 Minuten (früher cacheTime)
});
Query Keys identifizieren jede Query eindeutig. Sie sind Arrays, die enthalten können:
- Statische Identifikatoren:
['users']
- Dynamische Parameter:
['user', userId]
- Komplexe Filter:
['todos', { status, page }]
Wenn sich ein Teil des Query Keys ändert, weiß TanStack Query, dass frische Daten abgerufen werden müssen.
Mutationen: Server-State ändern
Während Queries Daten lesen, modifizieren Mutationen sie:
import { useMutation, useQueryClient } from '@tanstack/react-query';
function CreateTodo() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (newTodo) =>
fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo),
headers: { 'Content-Type': 'application/json' },
}).then(res => {
if (!res.ok) throw new Error('Failed to create todo');
return res.json();
}),
onSuccess: () => {
// Invalidieren und neu abrufen
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
return (
<button
onClick={() => mutation.mutate({ title: 'New Todo' })}
disabled={mutation.isPending}
>
{mutation.isPending ? 'Creating...' : 'Create Todo'}
</button>
);
}
Mutationen handhaben den kompletten Lebenszyklus: Ladezustände, Fehlerbehandlung und Erfolgs-Callbacks. Der onSuccess
-Callback ist perfekt für die Aktualisierung Ihres Caches nach Änderungen.
Query-Invalidierung: Daten frisch halten
Invalidierung markiert Queries als veraltet und löst Hintergrund-Refetches aus:
// Alles invalidieren
queryClient.invalidateQueries();
// Spezifische Queries invalidieren
queryClient.invalidateQueries({ queryKey: ['todos'] });
// Mit exakter Übereinstimmung invalidieren
queryClient.invalidateQueries({
queryKey: ['todo', 5],
exact: true
});
So hält TanStack Query Ihre UI nach Mutationen synchron. Ein Todo ändern? Die Todos-Liste invalidieren. Einen Benutzer aktualisieren? Die Daten dieses Benutzers invalidieren.
TanStack Query in Ihrer React-App einrichten
Installieren Sie zunächst das Paket:
npm install @tanstack/react-query
Dann umhüllen Sie Ihre App mit dem QueryClient-Provider:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 1 Minute
gcTime: 5 * 60 * 1000, // 5 Minuten (früher cacheTime)
retry: 3,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourApp />
</QueryClientProvider>
);
}
Der QueryClient
verwaltet das gesamte Caching und die Synchronisation. Normalerweise erstellen Sie eine Instanz für Ihre gesamte App.
Erweiterte Muster für Produktions-Apps
Optimistische Updates
Aktualisieren Sie die UI sofort und synchronisieren Sie dann mit dem Server:
const mutation = useMutation({
mutationFn: updateTodo,
onMutate: async (newTodo) => {
// Ausgehende Refetches abbrechen
await queryClient.cancelQueries({ queryKey: ['todos'] });
// Vorherigen Wert speichern
const previousTodos = queryClient.getQueryData(['todos']);
// Optimistisch aktualisieren
queryClient.setQueryData(['todos'], old =>
old ? [...old, newTodo] : [newTodo]
);
// Kontext für Rollback zurückgeben
return { previousTodos };
},
onError: (err, newTodo, context) => {
// Bei Fehler zurückrollen
if (context?.previousTodos) {
queryClient.setQueryData(['todos'], context.previousTodos);
}
},
onSettled: () => {
// Immer nach Fehler oder Erfolg neu abrufen
queryClient.invalidateQueries({ queryKey: ['todos'] });
},
});
Abhängige Queries
Verketten Sie Queries, die voneinander abhängen:
const { data: user } = useQuery({
queryKey: ['user', email],
queryFn: () => getUserByEmail(email),
});
const { data: projects } = useQuery({
queryKey: ['projects', user?.id],
queryFn: () => getProjectsByUser(user.id),
enabled: !!user?.id, // Nur ausführen, wenn Benutzer-ID existiert
});
Infinite Queries
Handhaben Sie paginierte Daten mit unendlichem Scrollen:
import { useInfiniteQuery } from '@tanstack/react-query';
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: ({ pageParam = 0 }) => fetchProjects({ page: pageParam }),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
initialPageParam: 0,
});
Häufige Fallstricke und Lösungen
Den QueryClientProvider vergessen
Wenn Ihre Queries undefined zurückgeben oder überhaupt nicht funktionieren, prüfen Sie, ob Ihre App mit QueryClientProvider
umhüllt ist. Das ist der häufigste Setup-Fehler.
TanStack Query für lokalen State verwenden
TanStack Query ist für Server-State gedacht. Verwenden Sie es nicht für Formulareingaben, UI-Toggles oder anderen rein clientseitigen State. Bleiben Sie bei useState
oder useReducer
für diese.
Statische Query Keys mit dynamischen Daten
Schließen Sie immer dynamische Parameter in Ihre Query Keys ein:
// ❌ Falsch - zeigt dieselben Daten für alle Benutzer
useQuery({
queryKey: ['user'],
queryFn: () => fetchUser(userId),
});
// ✅ Richtig - eindeutiger Cache-Eintrag pro Benutzer
useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
Fazit
TanStack Query verwandelt React-Datenabruf von einem manuellen, fehleranfälligen Prozess in ein deklaratives, robustes System. Indem es Server-State als grundlegend verschieden von Client-State behandelt, eliminiert es Boilerplate und fügt gleichzeitig mächtige Features wie Caching, Synchronisation und optimistische Updates hinzu.
Die mentale Umstellung ist einfach: Hören Sie auf zu denken, wie Sie Daten abrufen, und fangen Sie an zu denken, welche Daten Sie benötigen. Deklarieren Sie Ihre Anforderungen durch Query Keys und Funktionen und lassen Sie TanStack Query die Komplexität der Synchronisation handhaben.
Fangen Sie klein an – ersetzen Sie einen useEffect
-Abruf durch useQuery
. Sobald Sie die sofortigen Ladezustände, automatischen Wiederholungen und nahtloses Caching in Aktion sehen, werden Sie verstehen, warum TanStack Query für moderne React-Anwendungen unverzichtbar geworden ist.
FAQs
Es ist dieselbe Bibliothek. React Query wurde in TanStack Query umbenannt, um zu reflektieren, dass sie jetzt mehrere Frameworks jenseits von React unterstützt. Das React-spezifische Paket wird weiterhin gepflegt und verwendet dieselbe API, die Sie kennen.
Ja, aber Sie brauchen es wahrscheinlich nicht für Server-State. TanStack Query handhabt Caching und das Teilen von Daten zwischen Komponenten automatisch. Verwenden Sie Redux oder Context für echten Client-State wie Benutzereinstellungen oder UI-State, und TanStack Query für alles, was von einer API kommt.
Standardmäßig rufen Queries bei Fensterfokus, Wiederverbindung und Mount neu ab. Sie steuern die Frische mit staleTime (wie lange Daten frisch bleiben) und gcTime (wie lange ungenutzte Daten behalten werden). Veraltete Queries werden im Hintergrund neu abgerufen, während gecachte Daten sofort angezeigt werden.
Invalidierung markiert Queries als veraltet, ruft aber nicht sofort neu ab – sie wartet darauf, dass die Query wieder verwendet wird. Direktes Refetching löst eine sofortige Netzwerkanfrage aus. Verwenden Sie Invalidierung nach Mutationen für bessere Performance, da sie nur Queries neu abruft, die tatsächlich angezeigt werden.
TanStack Query eignet sich hervorragend für nahezu Echtzeit-Updates durch Polling und Refetch-Intervalle. Für echte Echtzeit-Bedürfnisse kombinieren Sie es mit WebSockets: Verwenden Sie den Socket für Live-Updates und TanStack Query für das initiale Laden von Daten und als Fallback, wenn der Socket getrennt wird.
Ja. TanStack Query 5+ ist vollständig kompatibel mit React 19. Die hier verwendeten APIs (Queries, Mutationen, Invalidierung) sind unverändert, sodass die Beispiele in diesem Artikel ohne Änderungen funktionieren werden.