Kontext, Zustand oder Redux Toolkit: Auswahl nach Komplexität der Übertragung
Wählen Sie React-State-Tools nach Komplexität der Übergänge und nicht nach Größe der Anwendung aus: Context verteilt Abhängigkeiten im Baumstruktur, Zustand präzisiert Abonnements, Redux Toolkit modelliert Übergänge.
Quellen: Vibe Coding Can Build the Store. Can You Design It?
KI hat einen Teil der Frontend-Arbeit fast kostenlos gemacht: die Erstellung des Codes. Wenn man nach einem Zustand-Store, einer Redux-Slice oder Context zusammen mit useReducer fragt, erzeugt ein Modell etwas, das kompiliert werden kann. Die Syntax ist nicht mehr der schwierige Teil – der schwierige Teil ist ein leeres Architekturkonzept sowie die Frage, wie der Zustand durch das Produkt fließen soll.
Das ist das eigentliche Problem der Zustandsverwaltung. Context API, Zustand und Redux Toolkit angehen es aus unterschiedlichen Richtungen. Es geht hier nicht um technische Details der API – sondern darum, was dahintersteckt.
1. Context API: Der React-Tree ist die Grenze
Context beantwortet eine spezifische Frage: Wie kann ein Wert tief liegende Komponenten erreichen, ohne bei jeder Ebene Prop-Drilling anwenden zu müssen?
In Reacts Dokumentation wird useContext als Mechanismus beschrieben, mit dem ein Kontext gelesen und überwacht wird. Ein Komponente erhält den Wert vom nächstgelegenen passenden Provider über sich. Wenn sich der Wert dieses Providers ändert, werden die Leser aktualisiert.
Der entscheidende Aspekt ist nicht der Aufruf des Hooks – es handelt sich vielmehr um die Baumbeziehung.
Der Provider definiert eine Umgebung; der Verbraucher liest daraus ab.
Kontexte existieren innerhalb der React-Hierarchie.
Kontexte verwalten keinen Zustand eigenständig
Eine häufige Fehlvorstellung ist, dass Context eine Zustellsammlung sei. Aktualisierungen stammen in der Regel von useState, Reduzern oder benutzerdefinierten Hooks. Context überträgt hauptsächlich die Werte, die diese Hooks erzeugen.
Die große Kontextfalle
Menschen sagen: „Der Kontext verursacht unnötige Neuberechnungen.“ Dieser Slogan ist zu pauschal. React vergleicht den vorherigen und nachfolgenden value eines Providers mit Object.is. Wenn sich der Wert ändert, wird jeder Leser aktualisiert; eine memo-Grenze blockiert keinen neuen Kontext.
Betrachten wir eine Sammlung unzusammenhängender Felder:
<AppContext
value={{
user,
cart,
orders,
notifications
}}
>
Das tiefere Problem ist architektonischer Natur: Es gibt eine einzige weite Abhängigkeitsgrenze für Domänen, die nicht zusammengehören.
Anstelle von:
AppContext
besser:
AuthContext
ThemeContext
FeatureFlagContext
Jeder Kontext hat dann eine klare semantische Aufgabe. Gestaltungsregel: Ein Kontext sollte eine sinnvolle Abhängigkeitsgrenze darstellen. Der Kontext ist an sich nicht „bei Skalierung schlecht“ – oft ist es ein riesiger Kontext.
2. Verlassen Sie den Baum: Zustand
Der Kontext ist mit dem Komponentenbaum verbunden. Zustand stellt eine andere Frage: Wie kann ein gemeinsamer Client-Zustand außerhalb dieses Baums mit präzisen Abonnements existieren?
Ein Store kann den Zustand sowie die Funktionen, die ihn ändern, speichern:
const useCartStore = create((set) => ({
items: [],
addItem: (item) =>
set((state) => ({
items: [...state.items, item]
})),
removeItem: (id) =>
set((state) => ({
items: state.items.filter(
item => item.id !== id
)
}))
}));
Komponenten wählen bestimmte Teile aus:
const items = useCartStore(
state => state.items
);
Der Vorteil liegt in der Abonnementgrenze. Die Komponente bittet nicht um den gesamten Store, sondern nur um den Teil, der für sie relevant ist. Selektoren machen diesen Vertrag explizit.
Warum Redux immer noch verwendet wird
Falls Zustand so einfach ist – warum dann Redux Toolkit? Weil das Problem von „Wo soll ich diesen Wert platzieren?“ zu „Wie gehe ich mit allem um, was ihn ändern kann?“ werden kann.
Stellen Sie sich überlappende Domänen vor:
Checkout
Payment
Inventory
Orders
Refunds
Permissions
Ein Workflow könnte so aussehen:
Checkout Started
↓
Payment Started
↓
Payment Succeeded
↓
Inventory Reserved
↓
Order Created
Falls die Reservierung des Inventars nach einem erfolgreichen Zahlungsvorgang fehlschlägt, liegt das Problem nicht mehr im Speichern – es handelt sich um ein State-Transition-Problem. Genau hier kommt die Struktur von Redux zum Tragen.
3. Redux als explizite Übergänge
Es ist nicht sinnvoll, Redux einfach als „globale Datenbank“ zu betrachten. Man sollte es vielmehr als ein explizites System sehen, das Ereignisse beschreibt und vorhersehbare nächste Zustände berechnet:
nextState =
reducer(previousState, action)
Redux-Fluss
Neben der einfachen Anweisung set(...) wirkt das hier aufwendig:
set(...)
Jeder Schritt hat weiterhin eine Aufgabe:
- Aktion – Was ist passiert?
- Reducer – Wie sollte sich der Zustand ändern?
- Store – Was ist der aktuelle Zustand?
- Selector – Was benötigt dieses Komponente?
Diese Explizitheit ist das Besondere. Wenn Geschäftsregeln kompliziert werden, hilft es, zu wissen warum sich der Zustand geändert hat – das ist oft wertvoller als das Speichern von zehn Zeilen Code.
Redux Toolkit ist modernes Redux
Klassisches Redux erforderte manuelle Action-Typen und -Ersteller. Toolkit vereinfacht das mit createSlice:
const cartSlice = createSlice({
name: "cart",
initialState: {
items: []
},
reducers: {
addItem(state, action) {
state.items.push(action.payload);
}
}
});
Verwandter Zustand und Übergänge bleiben zusammen; Action-Ersteller werden automatisch generiert. Immer ermöglicht eine mutationsähnliche Syntax, während gleichzeitig unveränderliche Updates erzeugt werden. Modernes Redux bedeutet weniger „Boilerplate schreiben“ und mehr „einen strukturierten Übergangsmodell ohne die historischen Formalitäten beibehalten“.
Middleware und Asynchronität
Middleware befindet sich zwischen dispatch und reduce:
Dieser Slot beherbergt die Protokollierung, Analytik, asynchrone Workflows sowie Reaktionen auf spezifische Aktionen. Toolkit fügt Hilfsfunktionen wie createAsyncThunk und Listener-Middleware hinzu. Zustand lässt die asynchrone Organisation offen; Toolkit bietet etablierteere Muster.
Welches soll man wählen?
Mieden Sie die Größenheuristik:
- Kleine App → Context
- Mittlere Größe → Zustand
- Große App → Redux
Anzahl der Bildschirme ist kein guter Indikator für die Komplexität des Zustands. Eine CRUD-App mit hundert Bildschirmen kann einfacher sein als ein Prozess für Zahlungen und Berechtigungen mit zehn Bildschirmen.
Betrachten Sie lieber Beziehungen und Übergänge:
Mentales Modell zum Beibehalten
Merken Sie sich nicht drei Logos – merken Sie sich stattdessen drei Ansätze:
- Context – verteilen Sie eine Abhängigkeit über den React-Tree
„Welche Bibliothek ist die beste?“ ist die falsche erste Frage. Besser: Welche Art von Komplexität muss gemanagt werden? Wählen Sie die Abstraktion, die es ermöglicht, mit dieser Komplexität besser umzugehen – nicht einfach jene, die als „modern“ bezeichnet wird.
In der Praxis mischen viele Codebasen diese Bibliotheken: Context für Theme und Lokalisation, Zustand für Warenkörbe und UI-Elemente, Toolkit nur dort, wo mehrstufige Prozesse im Umgang mit Geld und Lagerbeständen eine Nachverfolgbarkeit erfordern. Diese Kombination ist in Ordnung, solange jede Grenze bewusst festgelegt ist. Probleme entstehen, wenn ein einziger Mega-Context oder ein ununterschiedener Store alle Aufgaben übernimmt, weil eine Empfehlung vorschlug, für alles nur eine Bibliothek zu verwenden.