TanStack Query: Server-State-Management und Datenabruf in React

Wer in React-Anwendungen Daten von einer API laden möchte, stand lange vor einer Entscheidung: Entweder wird der State manuell mit useEffect und useState verwaltet – mit all den Fallen rund um Race Conditions, Ladezustände und Cache-Management – oder es wird ein globales State-Management-Tool wie Redux bemüht, das für diesen Anwendungsfall schlicht überdimensioniert ist. TanStack Query, früher bekannt als React Query, hat dieses Problem grundlegend gelöst.

Was ist TanStack Query?

TanStack Query ist eine Bibliothek für das asynchrone State-Management in React und anderen JavaScript-Frameworks. Die Kernidee: Server-seitiger State – also Daten, die von einer API kommen – unterscheidet sich grundlegend von Client-seitigem State wie Formularwerten oder UI-Zuständen. Server-State kann sich ändern ohne das Wissen des Clients, er ist asynchron und muss regelmäßig synchronisiert werden.

TanStack Query übernimmt genau diese Komplexität: Caching, Background-Refetching, Fehlerbehandlung, Ladezustände und Deduplizierung von Anfragen – alles out of the box, ohne eine einzige Zeile manuellen Cache-Managements.

Die Bibliothek unterstützt neben React auch Vue, Solid, Svelte und Angular. Das GitHub-Repository zählt über 45.000 Stars und ist damit eines der einflussreichsten Projekte im React-Ökosystem.

Stale-While-Revalidate-Strategie

Das zentrale Konzept hinter TanStack Query ist die Stale-While-Revalidate-Strategie. Dabei wird gecachte Daten sofort an die Komponente zurückgegeben (auch wenn sie "veraltet" sind), während gleichzeitig im Hintergrund eine neue Anfrage gestellt wird. Sobald die frischen Daten ankommen, wird die Komponente automatisch neu gerendert.

Diese Strategie führt zu zwei wichtigen Vorteilen: Die Benutzeroberfläche zeigt niemals unnötige Ladeindikatoren für Daten, die bereits gecacht sind, und gleichzeitig sind die angezeigten Daten immer möglichst aktuell. Das Ergebnis ist eine deutlich flüssigere Benutzererfahrung im Vergleich zu klassischen Loading-Spinner-Ansätzen.

useQuery: Daten laden

Der grundlegende Hook zum Abrufen von Daten ist useQuery:

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

function NutzerProfil({ nutzerId }) {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['nutzer', nutzerId],
    queryFn: () => fetch(`/api/nutzer/${nutzerId}`).then(r => r.json()),
    staleTime: 5 * 60 * 1000, // 5 Minuten
  });

  if (isLoading) return <Ladeanzeige />;
  if (isError) return <Fehlermeldung error={error} />;

  return <div>{data.name}</div>;
}

Das queryKey-Array ist der Cache-Schlüssel. Wenn sich ein Teil des Schlüssels ändert (z.B. die nutzerId), wird automatisch eine neue Anfrage gestellt. Mehrere Komponenten, die denselben queryKey verwenden, teilen sich den gleichen Cache-Eintrag – die API wird nur einmal aufgerufen.

useMutation: Daten verändern

Für schreibende Operationen steht useMutation bereit:

import { useMutation, useQueryClient } from '@tanstack/react-query';

function NutzerBearbeiten({ nutzerId }) {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: (neuerName) =>
      fetch(`/api/nutzer/${nutzerId}`, {
        method: 'PATCH',
        body: JSON.stringify({ name: neuerName }),
      }).then(r => r.json()),
    onSuccess: () => {
      // Cache für diesen Nutzer invalidieren
      queryClient.invalidateQueries({ queryKey: ['nutzer', nutzerId] });
    },
  });

  return (
    <button onClick={() => mutation.mutate('Max Mustermann')}>
      {mutation.isPending ? 'Wird gespeichert…' : 'Speichern'}
    </button>
  );
}

QueryKey-Caching und Invalidierung

TanStack Query verwaltet einen In-Memory-Cache aller abgerufenen Daten. Jeder Cache-Eintrag wird durch seinen queryKey identifiziert. Der QueryClient bietet Methoden zur Steuerung dieses Caches:

Background-Refetching

TanStack Query führt automatisch Re-Fetches in mehreren Situationen durch:

All diese Verhaltensweisen lassen sich pro Query oder global konfigurieren. Für Daten, die sich selten ändern (z.B. eine Liste von Ländern), kann staleTime: Infinity gesetzt werden, um Re-Fetches zu deaktivieren.

Tipp: Strukturieren Sie Ihre queryKey-Arrays hierarchisch: ['nutzer'] als Basis, ['nutzer', 'liste'] für Listen und ['nutzer', nutzerId] für einzelne Einträge. Mit invalidateQueries({ queryKey: ['nutzer'] }) können dann alle nutzer-bezogenen Queries auf einmal invalidiert werden.

Optimistische Updates

Optimistische Updates erlauben es, die UI bereits vor der API-Antwort zu aktualisieren, um eine responsivere Benutzererfahrung zu erzielen. Bei einem Fehler wird der vorherige Zustand automatisch wiederhergestellt:

const mutation = useMutation({
  mutationFn: updateNutzer,
  onMutate: async (neuerNutzer) => {
    await queryClient.cancelQueries({ queryKey: ['nutzer', neuerNutzer.id] });
    const vorheriger = queryClient.getQueryData(['nutzer', neuerNutzer.id]);
    queryClient.setQueryData(['nutzer', neuerNutzer.id], neuerNutzer);
    return { vorheriger };
  },
  onError: (err, neuerNutzer, context) => {
    queryClient.setQueryData(
      ['nutzer', neuerNutzer.id],
      context.vorheriger
    );
  },
  onSettled: (nutzer) => {
    queryClient.invalidateQueries({ queryKey: ['nutzer', nutzer.id] });
  },
});

DevTools

TanStack Query bietet offizielle DevTools, die als React-Komponente in die Applikation eingebunden werden. Sie zeigen alle aktiven Queries, deren Status, Daten und Cache-Zeitstempel in einem übersichtlichen Panel. Im Produktionsbuild können die DevTools einfach ausgelassen werden:

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

// In der App-Komponente:
{process.env.NODE_ENV === 'development' && (
  <ReactQueryDevtools initialIsOpen={false} />
)}

Vergleich: TanStack Query vs. SWR vs. RTK Query vs. Apollo Client

Merkmal TanStack Query SWR RTK Query Apollo Client
Bundle-Größe (min+gz) ~13 kB ~5 kB ~10 kB (Teil von RTK) ~33 kB
Framework-Support React, Vue, Solid, Svelte, Angular React React (via Redux) React, Vue, Angular
Optimistische Updates Ja (vollständig) Ja (einfacher) Ja (vollständig) Ja (vollständig)
Mutations useMutation (erstklassig) Manuell via useSWRMutation Erstklassig useMutation (GraphQL)
DevTools Offiziell, sehr gut Keine offiziellen Redux DevTools Apollo DevTools (Browser Extension)
API-Protokoll Agnostisch (REST, GraphQL, etc.) Agnostisch REST-optimiert GraphQL-only
Infinite Scroll / Pagination useInfiniteQuery (eingebaut) useSWRInfinite Eingebaut fetchMore
Achtung: TanStack Query löst Server-State-Probleme hervorragend, ist aber kein Ersatz für Client-State-Management. Formularzustände, UI-Toggles, modaler Zustand und andere reine Client-States gehören weiterhin in useState, useReducer oder leichtgewichtige Stores wie Zustand. Der Versuch, alles in TanStack Query zu verwalten, führt zu unnötiger Komplexität.

Einrichtung

TanStack Query wird über npm installiert und erfordert einen QueryClientProvider an der Wurzel der Applikation:

npm install @tanstack/react-query

// main.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

ReactDOM.createRoot(document.getElementById('root')).render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>
);

Die vollständige Dokumentation aller Hooks, Optionen und Muster findet sich in der offiziellen TanStack Query Dokumentation.

Häufig gestellte Fragen

Brauche ich noch Redux, wenn ich TanStack Query verwende?
In den meisten Fällen nicht mehr für Server-State. TanStack Query übernimmt das gesamte Caching und die Synchronisation von API-Daten. Für komplexen globalen Client-State (z.B. mehrstufige Wizard-Formulare, Shopping-Cart-Logik) können Redux oder alternative Stores wie Zustand weiterhin sinnvoll sein.
Wie lange bleiben Daten im Cache?
Standardmäßig werden Daten 5 Minuten nach dem letzten Abonnement im Cache behalten (gcTime). Daten gelten nach 0 Millisekunden als "stale" (veraltet), was bedeutet, dass bei jedem Mount ein Re-fetch ausgelöst wird. Beide Werte lassen sich global und pro Query anpassen.
Funktioniert TanStack Query mit Server Components in Next.js?
TanStack Query ist eine Client-seitige Bibliothek und funktioniert in React Client Components. Mit dem Hydration-Konzept lassen sich Queries jedoch serverseitig prefetchen und der Cache-Zustand an den Client übertragen, was die Kombination mit Next.js App Router ermöglicht. Die Dokumentation enthält eine ausführliche Next.js-Anleitung.
Kann TanStack Query auch für Nicht-HTTP-Datenquellen verwendet werden?
Ja. Die queryFn kann jede asynchrone Funktion sein – IndexedDB-Zugriffe, WebSocket-Daten, lokale Berechnungen oder Datenbankabfragen in Electron-Apps. TanStack Query ist vollständig protokoll-agnostisch und verhält sich immer gleich, unabhängig von der Datenquelle.