React Query und Redux: Eine Neubetrachtung des Server-Zustands in großen Anwendungen
Erfahren Sie, warum eine Produktions-Chat-App TanStack Query anstelle von Redux verwendet hat, um Serverdaten zu verwalten, und wo Redux in der modernen React-Architektur weiterhin eine Rolle spielt.
Stellen Sie sich einen Entwickler vor, der von kleineren Projekten zu einem produktbasierten Unternehmen wechselt und unbedingt sehen möchte, wie Software in großem Maßstab und für die Produktion tatsächlich entwickelt wird. Nach Jahren der Arbeit an Frontend-Anwendungen verändert das Beitreten zu einem Team, das Software für Millionen von Menschen entwickelt, die Art und Weise, wie man Code bewertet.
Man fragt nicht mehr einfach nur: „Funktioniert diese Funktion?“
Stattdessen stellt man folgende Fragen:
Wie hält diese Architektur bei Millionen von Nutzern stand? Wie wird der Zustand in der gesamten Anwendung synchron gehalten? Was passiert, wenn zehn verschiedene Komponenten alle denselben Datensatz benötigen? Wie halten Ingenieure eine so große Codebasis wartbar, während das Produkt weiter wächst?
Stellen Sie sich nun vor, diesem Entwickler wird eines der Kernmodule des Produkts zugeteilt – eine Chat-Anwendung, die im Geist dem Slack ähnelt.
Es handelt sich dabei nicht um eine unbedeutende Funktion, die irgendwo in einer Ecke der App versteckt ist.
Die Chattenfunktion steht im Zentrum des Produkts, dient Millionen von Nutzern und ist eng mit wichtigen Geschäftsabläufen, ertragssteigernden Funktionen sowie einigen der wichtigsten Unternehmenskunden des Unternehmens verbunden.
Deshalb ist es verständlich, dass dieser Entwickler bei seinem ersten Blick in die Codebasis durchaus vorhersehbare Erwartungen hatte.
Eine Chatschnittstelle in diesem Umfang muss unweigerlich mit einer enormen Menge an Informationen umgehen, die von vielen Bildschirmen geteilt werden müssen: Chatverläufe und ihre Nachrichten, die Anzahl der noch ungelesenen Nachrichten, wer an einer Konversation teilnimmt, das Durchblättern der Historie, Bearbeitungen und andere Schreibvorgänge, Ladeindikatoren während des Ladens sowie Echtzeit-Updates.
Angesichts all dessen würde man in einer großen React-Codebasis auf die üblichen Komponenten stoßen:
Redux, Zustand oder zumindest eine Form der globalen State-Verwaltung.
Aber nach Durchsuchung des Codes findet sich kein Store.
Keine Redux-Einrichtung.
Kein Zustand.
Auch kein umfangreiches Context-Objekt, das die gemeinsamen Daten der Anwendung enthält.
Auf den ersten Blick könnte man leicht annehmen, beim Erkunden des Repositories sei etwas übersehen worden.
Doch bei genauerer Untersuchung wird klar, was tatsächlich die Verwaltung der gemeinsamen Daten übernimmt.
TanStack Query.
Das ist eine wirklich überraschende Entdeckung, wenn das mentale Modell dieser Bibliothek begrenzt ist.
Viele Entwickler betrachten React Query hauptsächlich als Werkzeug zum Abrufen von API-Daten, Caching von Antworten, Überwachen des Ladezustands und Neuladen bei Änderungen.
Doch in dieser produktionstauglichen Anwendung, die Millionen von Nutzern bedient, leistete der Abfrage-Cache weitaus mehr als das. Er fungierte als gemeinsamer Speicher für alle vom Server verwalteten Daten in der gesamten Anwendung.
Diese Beobachtung stellt eine seit langem bestehende Annahme bezüglich der Frontend-Architektur in Frage.
Möglicherweise ist die richtige Frage nicht:
"Warum gibt es hier keinen Redux-Store?"
Möglicherweise sollte sie lauten:
"Warum sollten überhaupt Daten, die vom Server verwaltet werden, in Redux gespeichert werden?"
Diese Frage eröffnet ein viel tieferes Gespräch darüber, wie wir Zustände in React-Anwendungen modellieren, und warum TanStack Query in vielen realen Systemen einen erheblichen Anteil der globalen Zustandsmechanismen, die Teams traditionell manuell entwickelt haben, schrittweise ersetzen kann.
Von Redux zu React Query
Es gab eine Zeit, in der das Einbinden eines API-Aufrufs in eine React-Anwendung wie eine einfache Aufgabe erschien.
Dann kam Redux ins Spiel.
Der API-Aufruf selbst blieb einfach – alles, was darum herum entstand, wurde kompliziert.
Man definierte eine Aktion.
Schrieb einen Reducer.
Fügte Lade- und Fehlerindikatoren hinzu.
Schickte die Aktion ab.
Speicherte die Antwort im Store.
Schrieb einen Selektor, um sie wieder abzurufen.
Vernetzte das Komponenten mit all dem.
Und dann fragt irgendwann nach Wochen oder Monaten unweigerlich jemand:
„Warum sieht diese Datenlage veraltet aus?“
Die Lösung ist meist wieder nur eine weitere Aktion, um einen Neuladen zu erzwingen.
Nachdem man diesen Zyklus wiederholt durchlaufen hat, wird ein unangenehmes Muster deutlich:
Weltweite State-Tools wurden häufig verwendet, um etwas zu verwalten, das von Anfang an nie wirklich eine Sache des Client-States war.
Der Backend war der eigentliche Besitzer der Daten.
Der Frontend nutzte sie lediglich.
Dieser Unterschied ist ein wesentlicher Grund dafür, warum TanStack Query zu einer so interessanten Alternative in modernen React-Anwendungen geworden ist.
Zuerst: Was genau ist React Query?
Bevor wir untersuchen, wie es den Bedarf an Redux verringert, lohnt es sich, ein häufiges Missverständnis aufzuklären.
TanStack Query, früher React Query genannt, ist kein Ersatz für useState, Redux oder Zustand.
Ihre Hauptaufgabe besteht darin, Serverstate zu verwalten – Daten, die außerhalb Ihrer React-Anwendung stammen und abgerufen, im Cache gespeichert, synchron gehalten, aktualisiert sowie schließlich als veraltet behandelt werden müssen.
React Query geht es nicht nur darum, eine Anfrage zu senden und die Antwort im lokalen State eines Komponenten abzulegen.
TanStacks eigene Dokumentation konzentriert sich insbesondere auf das Abrufen, Caching, Synchronisieren und Aktualisieren des Serverzustands durch die Bibliothek.
Betrachten Sie Daten wie folgt:
Users
Projects
Messages
Notifications
Orders
Analytics
Ihr Frontend besitzt tatsächlich keine dieser Informationen.
Das Backend hingegen schon.
React Query fungiert als Schicht zwischen Ihrer Benutzeroberfläche und diesem Backend und übernimmt die Verantwortung für den Lebenszyklus dieser Daten.
Im Kern dieses Designs befindet sich der Query Cache.
Laut TanStacks aktueller Dokumentation ist QueryCache die Speicherschicht für Abfragen – sie enthält deren Daten, Metadaten und Status. Ein QueryClient verwaltet diesen Cache und stellt die APIs bereit, die eine Anwendung verwendet, um darin zu lesen, ihn zu aktualisieren, Einträge ungültig zu machen und auf andere Weise damit zu interagieren.
Deshalb können mehrere unzusammenhängende Teile einer Anwendung denselben Abfragedienst aufrufen und konsistente Ergebnisse erhalten:
const { data } = useQuery({
queryKey: ['projects'],
queryFn: fetchProjects
})
React Query tut jedoch weitaus mehr als nur eine einzige Antwort zu speichern.
Es verwaltet Caching, Deduplizierung von Anfragen, Aktualitätsüberwachung, Hintergrundaufrufe zur Neuabfrage, Wiederholungslogik, Garbage Collection, Mutationen sowie Invalidation – alles als Teil desselben Systems.
Zum Beispiel nach einem Projektupdate:
const queryClient = useQueryClient()
await updateProject(project)
queryClient.invalidateQueries({
queryKey: ['projects']
})
Anstatt zehn separaten Komponenten manuell anweisen zu müssen, wie sie ihre lokale Kopie des Projekts aktualisieren sollen, teilen Sie einfach dem Abfragesystem mit:
"Die Serverdaten hinter dieser Abfrage könnten nicht mehr aktuell sein."
Von dort aus kümmert sich das Caching darum, sie erneut zu validieren.
Das ist die grundlegende Idee hinter TanStack Query.
Es versucht nicht, ein zweites Redux zu werden.
Dadurch erhält der Serverzustand sein eigenes Lebenszyklusmodell.
Sobald man den Serverzustand als grundlegend anders vom Clientzustand betrachtet, wird es viel einfacher zu verstehen, warum eine Anwendung, die auf den ersten Blick einen umfangreichen Redux-Store zu benötigen scheint, eigentlich gar keinen braucht.
Redux war nie das Problem
Ehrlich gesagt trägt Redux selbst hier keine Schuld.
Redux Toolkit bleibt weiterhin der offiziell empfohlene Ansatz zur Erstellung von Redux-Anwendungen, und Redux ist weiterhin nützlich, wenn eine Anwendung tatsächlich einen komplexen Clientseitenzustand, vorhersehbare Übergänge zwischen Zuständen, Middleware-Pipelines oder ein einheitliches Zustandsmodell benötigt.
Probleme entstehen, wenn Entwickler absolut alles in einen einzigen globalen Store packen.
Stellen Sie sich eine Anwendung wie diese vor:
{
user: {},
projects: [],
teams: [],
notifications: [],
orders: [],
products: [],
analytics: {},
theme: "dark",
sidebarOpen: true
}
Auf den ersten Blick scheint all das zur Anwendung zu gehören.
Doch dem ist nicht so.
Stellen Sie sich eine einfache Frage:
Wer besitzt eigentlich diese Daten?
Wird die Liste der Projekte von der React-Anwendung gesteuert?
Nicht wirklich.
Der Backend ist der eigentliche Besitzer.
Könnte ein anderer Benutzer eine Bestellung ändern, solange Ihre Browser-Tab geöffnet bleibt?
Sicherlich.
Könnte eine Benachrichtigung ohne jegliche Aktion in Ihrem React-Code erscheinen?
Ja, absolut.
Könnte der Server einseitig die Berechtigungen eines Benutzers widerrufen oder ändern?
Selbstverständlich.
Daher gehört ein erheblicher Teil dieses „Anwendungszustands“ überhaupt nicht dem Frontend.
Es handelt sich um den Serverzustand.
Der Serverzustand bringt eine völlig andere Kategorie an Herausforderungen mit sich.
Man muss ihn abrufen.
Man muss ihn cachen.
Man muss herausfinden, wann er veraltet ist.
Man muss ihn erneut abrufen.
Man muss ihn nach Änderungen synchronisieren.
Man muss Ladeindikatoren sowie Fehlerzustände verwalten.
Man muss Wiederholungsversuche sowie unterbrochene Netzwerkverbindungen berücksichtigen.
Genau diese Probleme soll TanStack Query lösen.
Der architektonische Wandel
Eine herkömmliche, auf Redux ausgerichtete Konfiguration ähnelt in der Regel diesem Ablauf:
API
↓
Async action / thunk
↓
Reducer
↓
Redux Store
↓
Selector
↓
React Component
Bei vielen API-basierten Anwendungen kann dieser Ablauf in etwas wie folgt umgestaltet werden:
API
↓
TanStack Query
↓
Query Cache
↓
React Component
Unter Papier betrachtet mag der Unterschied gering erscheinen.
Das ist er nicht.
Der eigentliche Wandel besteht darin, dass man nicht mehr alle für den Serverzustand benötigten Komponenten manuell erstellen muss.
Nehmen wir etwas Soziuelles wie eine Liste von Projekten.
Unter Redux würde man in der Regel damit beginnen:
const initialState = {
data: [],
loading: false,
error: null
}
Dann fügt man eine asynchrone Aktion hinzu:
dispatch(fetchProjects())
Darauf folgt die Reducer-Logik, die Folgendes abdeckt:
pending
fulfilled
rejected
Und schließlich ein Selector:
const projects = useSelector(
state => state.projects.data
)
Nun setzen wir das neben die Version von TanStack Query:
const { data, isPending, error } = useQuery({
queryKey: ['projects'],
queryFn: fetchProjects
})
Das ist nicht nur eine Verringerung der Anzahl der Codezeilen.
Die Abfrage selbst wird zur Abstraktion, die die Serverressource umhüllt.
Der Abfrageschlüssel benennt die überwachte Ressource.
Der Cache speichert das Ergebnis.
Die Abfrage überwacht ihren eigenen Status.
Und beliebig viele Komponenten können von diesemselben gespeicherten Wert ausgehen.
TanStack Query’s QueryCache existiert speziell dazu, um Abfrageresultate zusammen mit ihrem zugehörigen Zustand zu speichern, und QueryClient bietet die Schnittstelle zum Arbeiten mit diesem Cache.
Der Abfragecache ist im Grunde ein globaler Speicher für Serverdaten
Dies ist vermutlich das wichtigste Konzept hier.
Viele Entwickler hören den Satz:
"React Query hat einen Cache."
und gehen davon aus, dass damit gemeint ist:
"Es werden also nur API-Antworten gekachtet.
Aber es geht darüber hinaus.
Der Abfragecache wird effektiv zur gemeinsamen, einzigen Quelle der Wahrheit für den Serverzustand Ihrer Anwendung.
Stellen Sie sich drei separate Komponenten vor:
Dashboard
|
+── ProjectList
|
+── ProjectSidebar
|
+── RecentProjects
Jede von ihnen benötigt dieselben Daten, die durch folgendes identifiziert werden:
['projects']
Es ist nicht notwendig, die Serverdaten erst manuell in Redux zu übertragen und anschließend von allen drei Komponenten aus dem Store abzurufen.
Man sendet einfach die gleiche Anfrage dorthin, wo sie benötigt wird:
useQuery({
queryKey: ['projects'],
queryFn: fetchProjects
})
TanStack Query kümmert sich im Hintergrund um den Austausch und das Caching dieser Daten.
Die resultierende Architektur kann wie folgt aussehen:
React App
|
┌─────────┴─────────┐
| |
Client State Server State
| |
Redux / Zustand TanStack Query
| |
UI state Query Cache
Plötzlich wird das gesamte System viel einfacher zu verstehen.
Kann React Query Redux ersetzen?
In einigen Anwendungen ja, tatsächlich ist das möglich.
Aber hier liegt die entscheidende Nuance:
Es ersetzt Redux nicht, indem es eine überlegene Version von Redux ist.
Stattdessen beseitigt es die Notwendigkeit, sich für den Serverzustand auf Redux zu verlassen.
Das ist eine völlig andere Aussage.
In der eigenen Dokumentation von TanStack Query wird das Management des Server-Zustands ausdrücklich als ein eigenständiges Problem im Vergleich zum Management des Client-Zustands dargestellt, wobei darauf hingewiesen wird, dass der zu verwalterische Client-Zustand erheblich reduziert werden kann, sobald der Server-Zustand auf React Query übertragen wird.
Dort werden die Dinge aus architektonischer Sicht wirklich interessant.
Möglicherweise kommt man zu einer solchen Aufteilung:
Client State
theme
sidebar
selectedTab
modal
editor
filters
Zusätzlich:
Server State
users
projects
orders
notifications
products
analytics
Ab diesem Punkt werden die Wahl der Werkzeuge deutlich gezielter:
Client State → Redux / Zustand / Context / React
Server State → TanStack Query
Anstatt zu erwarten, dass ein einziger Store beide Aufgaben gleichzeitig übernimmt.
Aber Redux hat immer noch eine Aufgabe
Betrachten wir eine andere Art von Anwendung, etwas, das eher einem Design-Tool wie Figma ähnelt.
Möglicherweise muss man Zustände wie folgt speichern:
{
selectedLayer,
activeTool,
zoom,
canvasMode,
dragState,
undoStack,
redoStack
}
Nichts davon ist Server-Zustand.
Der Frontend besitzt ihn vollständig.
Er ändert sich synchron, direkt als Reaktion auf Benutzerinteraktionen.
Mehrere Teile der UI sind gleichzeitig davon abhängig.
Oft muss man komplexe Übergänge koordinieren, wenn ein Zustandswert einen anderen beeinflusst.
Genau in solchen Szenarien kommt ein spezieller Client-Zustandsmanager zum Tragen.
Auch die Dokumentation von TanStack Query macht diesen Punkt: Komplexer, von der UI gesteuerter Zustand, der nichts mit dem Server zu tun hat, rechtfertigt dennoch die Verwendung eines dafür konzipierten Client-Zustandswerkzeugs.
Daher lautet die Schlussfolgerung nicht „Replaced Redux überall durch React Query“. Das wäre eine Überkorrektur.
Und es gibt noch einen weiteren wichtigen Akteur: RTK Query
Es gibt noch eine weitere Besonderheit, die erwähnt werden sollte.
Redux Toolkit enthält bereits RTK Query, eine Schicht zur Datenerfassung und -caching, die speziell dafür entwickelt wurde, innerhalb von Redux-Anwendungen zu funktionieren.
Es kann automatisch Hooks erzeugen und sich um das Abrufen von Endpunkten, Ladezustände sowie das Caching kümmern – genauso wie TanStack Query es tut.
Die eigentliche Veränderung im Ökosystem ist also nicht einfach nur:
Redux → React Query
Es sieht eher wie dieser Entwicklungsverlauf aus:
Manual API state in Redux
↓
Dedicated server-state solutions
↓
TanStack Query / RTK Query / Apollo / SWR
Die Branche insgesamt wird allmählich der Erkenntnis näher, dass Server-State und Client-State grundlegend unterschiedliche Verantwortungsbereiche sind, die unterschiedliche Werkzeuge erfordern.
Sobald man diese Unterscheidung verinnerlicht hat, wird die gesamte State-Architektur deutlich leichter zu verstehen.
Die Regel, die ich jetzt anwende
Um zu entscheiden, ob ein Datensatz zum globalen State gehört, reicht eine einzige Frage aus:
Wer besitzt eigentlich diese Daten?
Falls die Antwort lautet:
Der Backend-Bereich
haben Sie fast sicher mit dem Serverzustand zu tun.
Falls die Antwort lautet:
Der Frontend-Bereich
haben Sie fast sicher mit dem Clientzustand zu tun.
Diese eine Frage weist je nach Fall in der Regel auf eine völlig andere Architektur hin.
Zum Beispiel:
Current user ────────── Server
Projects ────────────── Server
Orders ──────────────── Server
Notifications ───────── Server
Theme ───────────────── Client
Modal ───────────────── Client
Selected tab ────────── Client
Editor state ────────── Client
Sobald man es auf diese Weise darstellt, wird die richtige Struktur offensichtlich.
React Query tötet Redux nicht
Die Behauptung, „React Query ersetze Redux“, ist so formuliert etwas irreführend.
Was tatsächlich ändert, ist etwas Subtileres:
Entwickler werden besser darin, herauszufinden, mit welcher Zustandskategorie sie es eigentlich zu tun haben.
Redux war früher die Standardlösung für alles, einschließlich Serverantworten.
Selbst immer mehr Teams trennen heute:
Server state
↓
TanStack Query
von:
Client state
↓
Redux / Zustand / Context / React
Für viele moderne React-Codebasen beseitigt allein diese Trennung einen erheblichen Teil der Komplexität, die früher Redux selbst zur Last gelegt wurde.
Ziel ist es nicht, die Anzahl der verwendeten Bibliotheken zu minimieren.
Ziel ist vielmehr, aufzuhören, Infrastruktur für Probleme manuell zu entwickeln, bei denen bereits solide, speziell dafür konzipierte Abstraktionen vorhanden sind.
Nächstes Mal, wenn Sie auf einen überdimensionierten Redux-Slice stoßen, der mit API-antworten, Ladeindikatoren, Logik zur Cache-Invalidierung und Refetch-Aktionen vollgestopft ist, fragen Sie sich:
Brauchen Sie wirklich einen globalen State-Manager dafür, oder haben Sie einfach React Query innerhalb von Redux manuell neu implementiert?
Wie gehen Sie in Ihren Anwendungen damit um?
Bewahren Sie derzeit Serverdaten in Redux oder Zustand auf, verlassen Sie sich auf TanStack Query oder wenden Sie ganz andere Ansätze an?
Verwandte Artikel
- Zehn versteckte Fehler in React-Komponenten, die moderne Apps verlangsamen — Erfahren Sie zehn häufige Fehler bei React-Komponenten – von Lücken im semantischen HTML bis hin zu fehlender Memoisierung – sowie die notwendigen Lösungen, um Apps im Jahr 2026 schnell, zugänglich und fehlerfrei zu halten.
- Behebung von React-Prop-Overload durch Komposition und Slots — Erfahren Sie, warum konfigurationsintensive React-Props zu Wartungsaufwänden führen, und wie Inversion of Control, Komposition sowie Slots wirklich wiederverwendbare Komponenten ermöglichen.