Server State

TanStack Query

TanStack Query ist die fehlende Data-Fetching-Schicht für React. Es verwandelt Server-Daten in einen Cache, wobei Loading-States, Fehler, Refetching und Invalidation automatisch für dich übernommen werden.

intermediate14 min readUpdated 15. Sept. 2026
usePosts.ts
tsx
// usePosts.ts
import { useQuery } from "@tanstack/react-query";

async function fetchPosts() {
  const res = await fetch("/api/posts");
  if (!res.ok) throw new Error("Failed to fetch posts");
  return res.json();
}

export function usePosts() {
  return useQuery({
    queryKey: ["posts"],
    queryFn: fetchPosts,
  });
}
Früher
React Query
Framework
React, Vue, Svelte, Solid
Lesen mit
useQuery
Schreiben mit
useMutation
Cache-Key
queryKey
Devtools
Dediziertes Panel

Warum es wichtig ist

Warum Server-State ein eigenes Tool benötigt

Ein echter Cache für Server-Daten

Requests werden gecacht, dedupliziert und über Komponenten hinweg geteilt, sodass dieselben Daten nur einmal abgerufen werden.

Loading und Error inklusive

Jede Query stellt Status, Loading, Error und Data bereit, sodass du diese Flags nicht mehr manuell schreiben musst.

Aktualität durch Policies

Stale Time, Refetch on Focus und Background-Updates halten die Daten aktuell, ohne dass ständiger manueller Aufwand nötig ist.

Das Gesamtbild

Die drei Kernkonzepte hinter TanStack Query

Ein über Query Keys identifizierter Cache, eine Query-Funktion zum Abrufen der Daten und eine Invalidation, die den Cache aktuell hält.

Queries

Lesen

Ein Query Key zusammen mit einer Fetch-Funktion beschreibt einen Teil der Server-Daten und dessen Cache-Eintrag.

Mutations

Schreiben

useMutation handhabt Create-, Update- und Delete-Requests und kann den Cache nach erfolgreichem Abschluss aktualisieren.

Invalidation

Sync

Markiert gecachte Daten als veraltet (stale), sodass sie neu abgerufen werden, um die UI nach einer Änderung konsistent zu halten.

TanStack Query auf einen Blick

Das Herzstück von TanStack Query

useQuery

Abonniere einen gecachten Teil der Server-Daten über einen Key.

useMutation

Sende Create-, Update- und Delete-Requests inklusive Loading- und Error-State.

queryKey

Die Identität eines Cache-Eintrags, genutzt zum Lesen, Aktualisieren und Invalidieren.

staleTime

Zeitraum, in dem Daten als frisch gelten, bevor ein Background-Refetch erfolgt.

Invalidation

Markiert Queries als veraltet, damit sie bei Bedarf neu geladen werden.

Devtools

Inspiziere jede Query, ihren Status und den Cache in einem dedizierten Panel.

Eine kurze Geschichte

Die Entwicklung des Server-State-Managements

  1. 2019

    React Query veröffentlicht

    Eine kleine Library bringt Caching und Server-State-Management zum Data Fetching in React.

    19
  2. 2021

    React Query 3

    Breitere Akzeptanz und eine ausgereifte API machen es zu einem gängigen Standard.

    21
  3. 2022

    TanStack Query

    Die Library wird umbenannt und auf Vue, Svelte und Solid erweitert.

    22
  4. 2023

    TanStack Query 5

    Eine einfachere API, bessere TypeScript-Inferenz und ein kleineres Bundle.

    23
  5. Heute

    Der Standard für Server-State

    Eine weit verbreitete Schicht, die mit jeder Client-State-Library kombiniert werden kann.

    Heute

Der vollständige Leitfaden

TanStack Query: Alles was Sie wissen müssen

Was ist TanStack Query?

TanStack Query, früher bekannt als React Query, ist eine Library für das Server-State-Management. Sie geht davon aus, dass Daten, die von einer API abgerufen werden, sich grundlegend von dem State unterscheiden, den man in useState verwaltet. Server-Daten werden geteilt, gecacht, können veralten und müssen erneut abgerufen werden. Dies mit Effects und Flags zu steuern, ist mühsam und fehleranfällig.

TanStack Query bietet Server-Daten einen ordentlichen Cache. Sie beschreiben, wie ein bestimmter Datensatz abgerufen wird, und weisen ihm einen Key zu; die Library kümmert sich dann um Caching, Deduplizierung, Loading- und Error-States, Background-Refetching sowie die Invalidierung. Es ist die fehlende Data-Layer für React und lässt sich mit jeder Client-State-Library kombinieren.

Queries

Eine Query wird durch einen Key und eine Query-Funktion definiert. Der Key identifiziert den Cache-Eintrag; die Funktion ruft die Daten ab.

// api.ts
async function fetchPost(id: string) {
  const res = await fetch(`/api/posts/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}
// Post.tsx
import { useQuery } from "@tanstack/react-query";

export function Post({ id }: { id: string }) {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ["post", id],
    queryFn: () => fetchPost(id),
  });

  if (isLoading) return <p>Loading…</p>;
  if (isError) return <p>Error: {error.message}</p>;

  return <article>{data.title}</article>;
}

Jede Komponente, die denselben Key verwendet, teilt sich einen Cache-Eintrag. Wenn die Komponente also zweimal gemountet wird, erfolgt der Abruf nur einmal. Die Status-Flags werden bereitgestellt, sodass du diese niemals selbst schreiben musst.

Query-Keys und der Cache

Der Key ist die Identität des Cache-Eintrags und sollte jede Variable enthalten, von der der Fetch abhängt.

// keys.ts
useQuery({ queryKey: ["posts"], queryFn: fetchPosts });
useQuery({ queryKey: ["post", id], queryFn: () => fetchPost(id) });
useQuery({
  queryKey: ["posts", { page, status }],
  queryFn: () => fetchPosts({ page, status }),
});

Da Keys Arrays sind, können Sie eine ganze Gruppe mithilfe eines Präfixes invalidieren. Das Invalidieren von ["posts"] invalidiert gleichzeitig auch ["posts", { page: 1 }], was Cache-Updates vorhersehbar macht.

Aktualität und Refetching

Zwei Optionen steuern, wann Daten erneut abgerufen (refetched) werden.

// freshness.ts
useQuery({
  queryKey: ["posts"],
  queryFn: fetchPosts,
  staleTime: 60_000, // fresh for one minute
  gcTime: 5 * 60_000, // keep unused data for five minutes
});

staleTime legt fest, wie lange Daten als aktuell (fresh) betrachtet werden; solange sie aktuell sind, erfolgt kein Refetch im Hintergrund. gcTime bestimmt, wie lange ein nicht verwendeter Eintrag im Speicher bleibt, bevor er durch die Garbage Collection entfernt wird. Standardmäßig führen Queries beim Mounten, beim Fokus auf das Fenster und beim erneuten Verbindungsaufbau einen Refetch durch, wodurch die UI ohne manuellen Aufwand aktuell bleibt.

Mutations

useMutation kümmert sich um Schreibvorgänge: Erstellen, Aktualisieren und Löschen.

// AddPost.tsx
import { useMutation, useQueryClient } from "@tanstack/react-query";

export function AddPost() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: (title: string) =>
      fetch("/api/posts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ title }),
      }).then((r) => r.json()),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["posts"] });
    },
  });

  return (
    <button
      onClick={() => mutation.mutate("New post")}
      disabled={mutation.isPending}
    >
      {mutation.isPending ? "Saving…" : "Add post"}
    </button>
  );
}

Nach einer erfolgreichen Mutation signalisiert das Invalidieren der betroffenen Queries allen Consumern, dass die Daten neu geladen werden müssen. So bleibt der Server die „Source of Truth“ und manuelle Eingriffe in den Cache werden vermieden.

Optimistische Updates

Bei Aktionen, die sich unmittelbar anfühlen sollen, aktualisieren Sie den Cache, bevor der Server antwortet, und machen Sie die Änderungen im Fehlerfall rückgängig.

// optimistic.ts
useMutation({
  mutationFn: toggleLike,
  onMutate: async (postId) => {
    await queryClient.cancelQueries({ queryKey: ["post", postId] });
    const previous = queryClient.getQueryData(["post", postId]);

    queryClient.setQueryData(["post", postId], (old) => ({
      ...old,
      likes: old.likes + 1,
    }));

    return { previous };
  },
  onError: (_err, postId, context) => {
    queryClient.setQueryData(["post", postId], context.previous);
  },
  onSettled: (_data, _err, postId) => {
    queryClient.invalidateQueries({ queryKey: ["post", postId] });
  },
});

onMutate wendet die optimistische Änderung an und erstellt einen Snapshot des vorherigen Wertes, onError führt den Rollback durch und onSettled ruft die Daten erneut ab, um sie mit dem Server zu synchronisieren.

Pagination und Infinite Queries

TanStack Query bietet erstklassigen Support für paginierte und unendliche Daten. useQuery mit einer keepPreviousData-Option sorgt für reibungslose Seitenübergänge, und useInfiniteQuery verwaltet cursor-basierte Listen mithilfe einer fetchNextPage-Funktion. Da jede Seite Teil des Caches ist, erfolgt die Navigation vor und zurück augenblicklich.

Server-State gegenüber Client-State

Dies ist die Unterscheidung, durch die TanStack Query so richtig Sinn ergibt:

  • Server-State wird vom Server verwaltet, ist gemeinsam genutzt, asynchron und kann veralten. Hierfür nutzt man TanStack Query.
  • Client-State wird vom Browser verwaltet, ist synchron und lokal. Hierfür nutzt man useState, Context, Zustand oder Redux.

Beide in einem einzigen Store zu vermischen, ist die Quelle für viel Komplexität. Die Trennung – TanStack Query für die Daten, ein kleiner Store für den UI-State – hält beides simpel.

Best Practices

  • Nehmen Sie jede Variable in den Query Key auf.
  • Setzen Sie staleTime bewusst; der Standardwert Null führt zu sehr häufigen Refetches.
  • Nutzen Sie Mutations in Kombination mit Invalidation, anstatt den Cache überall manuell zu aktualisieren.
  • Halten Sie Query-Funktionen klein und platzieren Sie diese direkt beim entsprechenden Query.
  • Implementieren Sie Optimistic Updates nur dort, wo ein sofortiges Feedback entscheidend ist.
  • Nutzen Sie die Devtools, um den Cache-Status während der Entwicklung zu untersuchen.
  • Trennen Sie den Server-State in TanStack Query vom Client-State.

Häufige Fehler

  • Verwendung desselben Keys für verschiedene Inputs, was zu veralteten oder falschen Daten führt.
  • staleTime auf Null lassen, wodurch ständige Refetches ausgelöst werden.
  • Duplizieren von Server-Daten in einem globalen Store.
  • Vergessen, nach einer Mutation zu invalidieren, wodurch veraltete Daten angezeigt werden.
  • Schreiben von Optimistic Updates ohne einen Rollback-Pfad.
  • Behandeln von Query-Funktionen wie Komponenten und Aufrufen von Hooks innerhalb dieser Funktionen.

Wie geht es weiter?

TanStack Query ist der Standardweg für die Verwaltung von Serverdaten in modernen Apps. Kombinieren Sie es mit der Fetch API für den Request-Layer, einem Client-Store wie Zustand für den UI-State und Redux Toolkit, falls Sie einen strikten globalen State benötigen. Für Vue übernimmt Pinia den Client-State, während TanStack Query die Serverseite abdeckt.

Daten abrufen

useQuery cached, dedupliziert und stellt den State bereit. Die Effect-Variante ruft bei jedem Mount neu ab und benötigt manuelle Flags.

Bevorzugt
const { data, isLoading, error } = useQuery({
  queryKey: ["posts"],
  queryFn: fetchPosts,
});
Vermeiden
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
  fetch("/api/posts")
    .then((r) => r.json())
    .then(setData)
    .finally(() => setLoading(false));
}, []);

Daten aktuell halten

Invalidiere die Query über den Key nach einer Mutation, sodass jeder Consumer neu lädt. Keine manuellen Cache-Manipulationen.

Bevorzugt
const queryClient = useQueryClient();

const mutation = useMutation({
  mutationFn: addPost,
  onSuccess: () => {
    queryClient.invalidateQueries({
      queryKey: ["posts"],
    });
  },
});
Vermeiden
// refetching by hand in
// every component that
// happens to show posts

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, TanStack Query zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch TanStack Query — mit Quizzen und echtem Code, den Sie im Browser ausführen können.