React-Anwendungsarchitektur: Zustand, Daten und Routing

React-Apps werden selten durch React selbst schwer wartbar. Sie werden schwer wartbar, wenn drei Belange ineinander verschwimmen:
- Zustand mit unterschiedlichen Lebenszyklen (Server-Daten vs. UI-Toggles vs. URL-Zustand) wird am selben Ort gespeichert.
- Datenabruf und Mutationen geschehen ad hoc (Komponenten rufen
fetchdirekt auf, inkonsistentes Caching, keine Invalidierungsdisziplin). - Routing wird nur als Navigation behandelt, nicht als erstklassige Architekturoberfläche (Laden, Autorisierung, Grenzen, Code-Splitting).
Eine solide React-Anwendungsarchitektur macht diese Grenzen explizit. Das Ergebnis: weniger Regressionen, vorhersehbarere Performance und eine Codebasis, die Teams skalieren können, ohne alle 12 Monate neu zu schreiben.
Dieser Leitfaden konzentriert sich auf eine praktische Grundlage für React-Anwendungsarchitektur rund um Zustand, Daten und Routing und erklärt, wie diese zusammenpassen.
Das grundlegende Denkmodell: 4 Zustandsarten (nicht 1)
Wenn Sie nur eine Architekturegel aus diesem Artikel mitnehmen, dann diese:
Behandeln Sie „Zustand" nicht als eine Sache. Behandeln Sie ihn als separate Kategorien mit unterschiedlichen Sources of Truth.
Die 4 Kategorien, für die Sie entwerfen sollten
| Zustandskategorie | Source of Truth | Lebensdauer | Beispiel | Standardmäßiger Speicherort |
|---|---|---|---|---|
| Server-Zustand | Backend/API | Geteilt über Nutzer und Sessions (mit Caching) | Aktuelles Nutzerprofil, Rechnungsliste | Ein Server-Zustandscache (TanStack Query, RTK Query, SWR) |
| Client-UI-Zustand | Browser/Laufzeit | Lokal für eine Session | Sidebar offen, aktiver Tab, Dialog offen | Lokaler Komponentenzustand, kleiner Client-Store (Zustand/Jotai/Redux) |
| URL-Zustand | Die Route (Pfad + Query) | Teilbar/als Lesezeichen speicherbar | ?page=3&status=open, ausgewählte Entitäts-ID | Router-Zustand (React Router, Framework-Router) |
| Formularzustand | Das Formular (meist flüchtig) | Bis zum Absenden/Zurücksetzen | Validierungsfehler, geänderte Felder, Feld-Arrays | Formularbibliothek (React Hook Form) + Schema-Validierung (Zod) |
Wenn diese vermischt werden (z. B. Server-Zustand in Redux, „weil wir Redux schon haben"), zahlt man meist mit duplizierten Caches, inkonsistenten Ladezuständen und komplexer Invalidierung.
Wenn Sie einen tiefergehenden Feature-First-Modulansatz wünschen, geht Wolf-Techs Begleitartikel über React-Frontend-Architektur für Produktteams ausführlicher auf Ordnergrenzen, Abhängigkeitsregeln und Skalierungsmuster ein.
Routing: Routen als Architekturnaht behandeln, nicht nur als URLs
Routing bestimmt mehr als „welche Seite angezeigt wird". In einer Produktionsanwendung ist die Route der Ort, an dem Sie folgendes durchsetzen können:
- Datenabhängigkeiten (was muss geladen sein, bevor gerendert wird)
- Autorisierungsgrenzen (welche Berechtigungen/Mandantenkontext erforderlich ist)
- Fehlergrenzen (was graceful fehlschlägt vs. die gesamte Shell zum Absturz bringt)
- Code-Splitting (welche Bundles pro Bereich geladen werden)
- Caching-Richtlinie (besonders in Full-Stack-Frameworks)
Selbst in einer klassischen SPA (React + React Router) können Sie Routen als explizite „Einstiegspunkte" entwerfen, die die Orchestrierung übernehmen.
Eine praktische Checkliste für Routenverantwortlichkeiten
| Belang | Sollte auf Routenebene entschieden werden? | Warum es wichtig ist |
|---|---|---|
| Welche Daten zum Rendern benötigt werden | Ja | Hält Datenabhängigkeiten sichtbar und konsistent |
| Was bei fehlenden/ungültigen Params gilt | Ja | Verhindert verstreuten defensiven Code |
| Berechtigungsprüfungen und Weiterleitungen | Ja | Verhindert, dass Sicherheitslogik in zufälligen Komponenten landet |
| Fehler-UI für den Bereich | Ja | Begrenzt den Schadensradius (Widerstandsfähigkeit auf Bereichsebene) |
| Lazy Loading für den Bereich | Ja | Hält Bundles klein und vorhersehbar |
Für SPA-Routing-Muster sind React Routers Daten-APIs (Loader/Actions) empfehlenswert, weil sie routeneigene Datenorchestrierung fördern.
Datenarchitektur: Server-Zustand vorhersehbar und konsistent gestalten
Die meisten React-Apps verbringen mehr Zeit mit dem Warten auf I/O als mit „Rendern". Die wichtigsten Architekturentscheidungen liegen oft im Datenzugriff:
- Wie Sie abrufen
- Wie Sie cachen
- Wie Sie invalidieren
- Wie Sie Fehler und Wiederholungsversuche behandeln
- Wie Sie Typen/Verträge ausgerichtet halten
Standard: Eine Server-Zustands-Bibliothek verwenden
Wenn Ihre App mit einer API kommuniziert, möchten Sie fast immer eine dedizierte Server-Zustandsschicht. Es geht nicht um „State-Management-Präferenz", sondern darum, ein vorhersehbares System für folgendes zu haben:
- Anfragen deduplizieren
- Caching und Stale-while-Revalidate-Verhalten
- Hintergrund-Refetch
- Mutation und Invalidierung
- Wiederholung, Backoff, Abbruch
Gängige Optionen:
Wolf-Techs breitere Toolkit-Empfehlungen sind in React Tools: Das essentielle Toolkit für produktionsreife UIs zusammengefasst.
Entscheiden Sie Ihre „Datenvertrags"-Strategie frühzeitig
Ihre UI ist nur so stabil wie Ihre Verträge. Zwei pragmatische Standards:
- Contract-First-Schemas an der Grenze: Antworten mit einem Laufzeitschema (z. B. Zod) validieren und typisieren, damit API-Drift laut und früh fehlschlägt.
- Typisierte Abrufschicht: ein gemeinsamer HTTP-Client-Wrapper (Timeouts, Header, Fehlermapping), der von allen Features verwendet wird.
Wenn Sie das als konkretes Feature-Slice sehen möchten, zeigt Wolf-Techs React-Tutorial: Produktionsreifes Feature-Slice entwickeln ein sauberes Muster (Verträge, Hooks, Fehlerbehandlung, Telemetrie, Tests), ohne jeden Screen zu einem Framework zu machen.
Wie Zustand, Daten und Routing zusammenfließen sollten
Ein skalierbarer Ansatz macht die Route zum Orchestrierungspunkt, den Server-Zustandscache zur Source of Truth für Backend-Daten und UI-Zustand streng lokal.
Eine gute Architektur sieht typischerweise so aus:
- Route parst URL-Zustand (Params, Querystring).
- Route löst Datenanforderungen aus (Loader, Prefetch oder Route-Komponente, die Queries mountet).
- Server-Zustands-Bibliothek übernimmt Caching, Wiederholungsversuche und Invalidierung.
- UI-Komponenten rendern aus dem gecachten Server-Zustand plus lokalem UI-Zustand.
- Mutationen schreiben über die Server-Zustandsschicht und invalidieren oder aktualisieren dann Caches.

Ein pragmatisches Ordner- und Abhängigkeits-Layout (funktioniert für die meisten Produktteams)
React-Architektur-Debatten stecken oft beim Ordner-Bikeshedding fest. Wählen Sie stattdessen eine Struktur, die die Grenzen durchsetzbar macht.
Ein gängiger Ausgangspunkt für eine React-SPA:
src/app/für App-Shell, Routing, Provider und globale Initialisierungsrc/features/<feature>/für Feature-Module (Screens, lokale Hooks, lokale UI)src/shared/für wirklich gemeinsame UI-Primitive und Utilitiessrc/api/(odersrc/data/) für den HTTP-Client, die Query-Key-Factory und Contract-Schemas
Die entscheidende Regel ist nicht die Benennung, sondern die Richtung der Abhängigkeiten:
- Features können von
sharedundapiabhängen. sharedsollte nicht von Features abhängen.apisollte keine UI importieren.- Routen komponieren Features, nicht umgekehrt.
Wenn Sie einen teamfreundlichen Weg brauchen, um Standards schrittweise einzuführen, lesen Sie React Development Playbook: Standards für Teams.
Zustandsarchitektur: Was global sein sollte (und was nicht)
Standardregel: UI-Zustand lokal halten, bis es wehtut
Globale Stores sind leicht hinzuzufügen und schwer rückgängig zu machen. Eine einfache Heuristik:
- Wenn Zustand nur in einem Screen oder einem Feature verwendet wird, halten Sie ihn in diesem Feature.
- Wenn Zustand über viele unverbundene Features hinweg verwendet wird, machen Sie ihn global.
- Wenn Zustand per Link teilbar sein muss, legen Sie ihn in die URL.
Vermeiden Sie die „Ein Store für alles"-Falle
Ein großer Redux-Store, der enthält:
- API-Daten
- UI-Flags
- Router-ähnliche Dinge
…wird oft zu einer zweiten Anwendung innerhalb Ihrer Anwendung.
Eine bessere Aufteilung:
- Server-Zustand in TanStack Query (oder Äquivalent)
- UI-Zustand in lokalem Komponentenzustand, und kleine Stores nur bei Bedarf
- URL-Zustand im Router
- Formularzustand in einer Formularbibliothek
Diese Trennung verbessert auch das Testen: Sie können UI testen, ohne einen vollständigen globalen Store zu mocken, und Sie können Daten-Hooks mit einem vorhersehbaren Query-Client testen.
Routing-Muster, die spätere Komplexität vermeiden
Muster 1: Routenebene „Bereichs-Shells" mit Grenzen
In einem echten Produkt gibt es meist Bereiche mit unterschiedlichem Verhalten:
- Ein öffentlicher Marketingbereich
- Ein authentifizierter App-Bereich
- Ein Admin-Bereich
Behandeln Sie jeden als einen Bereich mit eigenem:
- Layout
- Fehlergrenze
- Auth-Guard
- Datenprefetch (optional)
Damit werden „App-Belange" nicht in falsche Bereiche durchgereicht.
Muster 2: URL-Zustand als Integrationspunkt für Listenansichten
Listen benötigen fast immer:
- Paginierung
- Sortierung
- Filter
- Eine ausgewählte Zeile
Machen Sie diese standardmäßig URL-gesteuert, damit:
- Nutzer Lesezeichen setzen und teilen können
- Vor/Zurück natürlich funktioniert
- Bugs aus einer kopierten URL reproduziert werden können
Ein praktischer Ansatz ist, Query-Params am Routeneinstieg zu parsen und typisierte Werte in Ihre Query-Hooks weiterzugeben.
Muster 3: Routenbasiertes Code-Splitting
Routenbasiertes Code-Splitting ist meist die renditereichste Form von Lazy Loading, weil es mit der Nutzernavigation übereinstimmt.
Wenn Sie React Router verwenden, können Sie Route-Elemente lazy laden. Wenn Sie ein Framework (Next.js, Remix) verwenden, kommt Code-Splitting oft „kostenlos" mit der Routengrenze.
Wenn Sie bewerten, ob Sie bei SPA bleiben oder zu einem React-Framework wechseln sollten, ist Wolf-Techs Next.js- und React-Entscheidungsleitfaden für CTOs ein praktischer Vergleich mit Fokus auf Einschränkungen (Performance, Sicherheitsgrenzen, Betrieb), nicht auf Hype.
Datenabruf und Mutationen: Die Regeln, die Sie aufschreiben wollen
Die meisten Teams benötigen eine kleine „Datenverfassung". Hier ist ein produktionstauglicher Ausgangspunkt.
1) Query-Keys und Invalidierung standardisieren
Wählen Sie ein Query-Key-Muster, das zu Ihrer Domain passt. Konzeptionelles Beispiel:
['projects', { orgId, filters }]['project', { projectId }]
Standardisieren Sie dann Invalidierungsregeln:
- Das Mutieren eines Projekts invalidiert
['project', { projectId }]und alle Listenkeys, die es enthalten können. - Das Mutieren einer Mitgliedschaft invalidiert Mitgliederlisten, nicht alles.
2) Mutationen hinter einer Feature-API kapseln
Ein gutes Muster ist, Feature-Level-Operationen bereitzustellen:
useInviteMemberMutation()useUpdateProjectMutation()
…und rohe HTTP-Aufrufe in src/api/ zu halten.
Das verhindert, dass Screens zum Ort werden, an dem man „herausfindet, wie das Backend funktioniert".
3) Fehlerbehandlung normalisieren
Nutzer interessiert es nicht, ob der Fehler von Axios, fetch, einem Proxy oder einem GraphQL-Gateway kam.
Normalisieren Sie Fehler in eine kleine Menge UI-relevanter Kategorien:
- Nicht autorisiert (Login/Refresh erforderlich)
- Verboten (Berechtigungsproblem)
- Nicht gefunden
- Validierungsfehler (Feldebene)
- Konflikt (veraltete Daten)
- Transient/Netzwerk (wiederholbar)
Dann definieren Sie, wo jede Kategorie behandelt wird:
- Routenebene für „kann diese Seite nicht betreten"-Probleme
- Feature-Ebene für „Absenden fehlgeschlagen"-Probleme
- Komponentenebene für Inline-Validierung
4) Ladeverhalten explizit gestalten
Ein häufiges Anti-Muster ist „jede Komponente zeigt ihren eigenen Spinner". Das erzeugt Flackern.
Entscheiden Sie stattdessen das Ladeverhalten an Grenzen:
- Route: Skelett für die Seite
- Feature: Inline-Platzhalter für einen Bereich
- Komponente: nur für wirklich kleine eingebettete Teile
Wenn Sie im Next.js App Router arbeiten, sind Lade- und Fehlergrenzen erstklassig. Wolf-Techs Next.js Best Practices für skalierbare Apps behandelt diese Muster im Detail.
Werkzeugauswahl: Eine Entscheidungstabelle, die ehrlich hält
Tool-Wildwuchs ist ein Architekturproblem. Hier ist ein pragmatisches Mapping, das in vielen Teams funktioniert.
| Belang | Bester Standard | Wann etwas anderes wählen |
|---|---|---|
| Server-Zustand | TanStack Query (oder RTK Query) | Wenn Sie einen GraphQL-Client mit normalisierten Cache-Anforderungen haben (Apollo, urql) |
| Client-UI-Zustand | Lokaler Zustand zuerst, dann Zustand/Jotai | Redux, wenn Sie wirklich zentralisiertes Event-Logging, komplexe Feature-übergreifende Workflows benötigen |
| URL-Zustand | Router-Params + Query-Parsing | Ein dedizierter URL-Zustand-Helper bei umfangreicher Querystring-Logik |
| Formulare | React Hook Form + Schema-Validierung | Formik nur bei tiefer Investition und Stabilität |
| Routing | React Router (SPA) | Next.js/Remix bei SSR, besseren Caching-Grenzen oder Framework-Konventionen |
Der Punkt sind nicht die spezifischen Bibliotheken, sondern die Trennung der Verantwortlichkeiten.
Häufige Fehlermodi (und die Lösung)
Fehlermodus: „Alles ist ein Hook innerhalb von Komponenten"
Symptome:
- Komponenten rufen
fetchdirekt auf - Keine konsistenten Wiederholungsversuche/Timeouts
- Caching ist zufällig
- Lade- und Fehler-UI ist inkonsistent
Lösung: Eine dünne Datenschicht einführen (api client + queries/mutations), und Features diese aufrufen lassen.
Fehlermodus: „Wir haben Redux für Server-Daten verwendet und es ist jetzt schwer"
Symptome:
- Duplizierte Caching-Logik
- Manueller Refetch, manuelle Invalidierung
- Inkonsistentes Verhalten bei veralteten Daten
Lösung: Server-Zustand schrittweise zu einer Server-Zustands-Bibliothek migrieren. Mit einem Feature beginnen und Reduzierung von Code und Bugs messen.
Fehlermodus: „Routen sind nur eine Liste von Komponenten"
Symptome:
- Auth-Checks über Screens hinweg dupliziert
- Querystring-Parsing dupliziert
- Keine Fehlergrenzen auf Bereichsebene
Lösung: Routen-Shells einführen (Layout + Guard + Grenze) und Orchestrierung zu Routen verschieben.
Eine leichtgewichtige „Architektur-Grundlage", die in 1 Sprint eingeführt werden kann
Wenn Sie einen Plan ohne Neu-Schreiben benötigen, streben Sie eine inkrementelle Grundlage an:
- Zustandskategorien definieren (Server, UI, URL, Formular) und sie in Ihre Team-Standards aufschreiben.
- Einen gemeinsamen API-Client-Wrapper hinzufügen (Timeouts, Auth-Header, Fehlermapping).
- Eine Server-Zustands-Bibliothek wählen und in einem Feature-Slice einführen.
- Einen Routenbereich in eine Bereichs-Shell mit Fehlergrenze und Auth-Guard umstrukturieren.
- Query-Key-Muster und Invalidierungsregeln für diesen Slice standardisieren.
Sobald die Grundlage vorhanden ist, wird Skalierung zur Wiederholung, nicht zur Neuerfindung.
Wann eine externe Architekturüberprüfung sinnvoll ist
Wenn Ihre React-App bereits ausgeliefert wird, ist das Ziel selten „neue Architektur". Es ist meist:
- Regressionsrate reduzieren
- Performance-Vorhersehbarkeit verbessern
- Feature-Arbeit günstiger machen
- Onboarding schneller machen
Wolf-Tech ist spezialisiert auf Full-Stack-Entwicklung und Architekturarbeit, einschließlich Code-Qualitätsberatung und Legacy-Optimierung. Wenn Sie ein zweites Paar Augen auf Ihre React-Anwendungsarchitektur möchten (Zustandsgrenzen, Datenschicht, Routing-Nähte und Änderungssicherheitspraktiken), können Sie Wolf-Tech unter wolf-tech.io erkunden und die relevanten Blog-Leitfäden als Ausgangspunkt für eine evidenzbasierte Überprüfung nutzen.

