schlankeres State Management ohne Big Bang
Redux loest heute noch valide Probleme, produziert aber in vielen Projekten unnoetigen Boilerplate. Wer schrittweise von Redux zu Zustand migriert, ersetzt Reducer, Actions und Middleware Slice fuer Slice, ohne die App in einem riskanten Rutsch umzuschreiben.
Inhaltsverzeichnis
- 1. Warum Teams von Redux zu Zustand migrieren
- 2. Migrationsstrategie: Slice fuer Slice statt Big Bang
- 3. Den ersten Store Slice migrieren
- 4. Selectors und Memoization ohne Reselect
- 5. Async Actions: von Thunks zu einfachen Funktionen
- 6. Middleware und Persistenz nachbilden
- 7. Devtools und Time Travel Debugging behalten
- 8. Redux und Zustand parallel betreiben
- 9. Redux und Zustand im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Teams von Redux zu Zustand migrieren
Redux war ueber Jahre die Standardantwort auf die Frage nach globalem State in React, aber viele Teams stellen fest, dass sie von Redux zu Zustand migrieren wollen, sobald der Boilerplate die eigentliche Fachlichkeit ueberdeckt. Ein einfacher Zaehler braucht in klassischem Redux eine Action, einen Action Creator, einen Reducer und einen Store Slice Eintrag. In Zustand ist derselbe Zaehler eine einzige Funktion mit einem Objekt, das State und Updater Funktionen kombiniert.
Ein zweiter Grund fuer die Migration ist die Bundle Groesse. Redux Toolkit inklusive React Redux Bindings wiegt deutlich mehr als Zustand, das ohne Provider, ohne Context und ohne zusaetzliche Bindings auskommt. Fuer Projekte, bei denen jedes Kilobyte im initialen Bundle zaehlt, ist das ein messbarer Vorteil, den Teams beim Vergleich der beiden Bibliotheken schnell erkennen.
Der dritte Grund ist die mentale Einstiegshuerde fuer neue Teammitglieder. Redux erfordert das Verstehen von Reducern, Immutability Patterns, Middleware und oft zusaetzlich Reselect fuer Selectors. Wer von Redux zu Zustand migriert, reduziert diese Konzepte auf ein einzelnes Store Objekt mit Funktionen, was die Einarbeitungszeit fuer neue Entwickler spuerbar verkuerzt.
2. Migrationsstrategie: Slice fuer Slice statt Big Bang
Eine Migration von Redux zu Zustand in einem einzigen Pull Request ist bei mittelgrossen bis grossen Anwendungen praktisch immer die falsche Entscheidung. Stattdessen migriert man Slice fuer Slice: ein fachlicher Bereich wie cart oder auth wird komplett auf Zustand umgestellt, waehrend der Rest der Anwendung weiterhin ueber Redux laeuft. Beide State Container koexistieren waehrend der gesamten Uebergangsphase im selben Baum.
Fuer die Reihenfolge gilt eine aehnliche Regel wie bei anderen Migrationen: Slices mit wenigen Abhaengigkeiten zu anderen Teilen des Stores zuerst, komplexe Slices mit vielen Cross Slice Selectors zuletzt. Ein Slice, der nur von einer einzigen Feature Komponente gelesen wird, laesst sich in Stunden migrieren. Ein Slice, der von zehn verschiedenen Komponenten quer durch die App gelesen wird, braucht deutlich mehr Planung und Testabdeckung, bevor er angefasst wird.
Wichtig ist, dass die oeffentliche Schnittstelle eines migrierten Slice zunaechst identisch bleibt. Komponenten, die bisher useSelector und useDispatch genutzt haben, sollten in einem Zwischenschritt auf einen eigenen Hook wie useCartStore umgestellt werden, der intern entweder Redux oder Zustand ansprechen kann. So bleibt die Migration fuer aufrufende Komponenten unsichtbar.
3. Den ersten Store Slice migrieren
Beim Uebertrag eines Redux Slice zu Zustand entfaellt die strikte Trennung zwischen Reducer und Action Creator. Zustand definiert State und die Funktionen, die ihn veraendern, in derselben Store Definition. Das reduziert die Anzahl der Dateien pro fachlichem Bereich haeufig von drei oder vier auf eine einzige.
// BEFORE: Redux Toolkit slice with actions, reducer and selectors
import { createSlice } from '@reduxjs/toolkit';
const cartSlice = createSlice({
name: 'cart',
initialState: { items: [] },
reducers: {
addItem: (state, action) => { state.items.push(action.payload); },
removeItem: (state, action) => {
state.items = state.items.filter(i => i.id !== action.payload);
},
clearCart: (state) => { state.items = []; },
},
});
export const { addItem, removeItem, clearCart } = cartSlice.actions;
export const selectCartTotal = (state) =>
state.cart.items.reduce((sum, i) => sum + i.price, 0);
export default cartSlice.reducer;
// AFTER: Zustand store combining state and actions in one place
import { create } from 'zustand';
export const useCartStore = create((set, get) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
removeItem: (id) => set((state) => ({
items: state.items.filter((i) => i.id !== id),
})),
clearCart: () => set({ items: [] }),
cartTotal: () => get().items.reduce((sum, i) => sum + i.price, 0),
}));
Ein haeufiger Fehler bei dieser Migration: Entwickler versuchen, den Redux Reducer Stil mit einer riesigen switch Anweisung in Zustand nachzubilden, statt die direkten Update Funktionen zu nutzen, die Zustand anbietet. Wer von Redux zu Zustand migriert, sollte diese Gelegenheit nutzen, um den Code tatsaechlich zu vereinfachen, statt nur die Syntax auszutauschen und die alte Denkweise beizubehalten.
4. Selectors und Memoization ohne Reselect
In Redux wird Reselect haeufig eingesetzt, um teure abgeleitete Werte zu memoizen und unnoetige Re Renders zu verhindern. Zustand loest dieses Problem nativ ueber Selector Funktionen, die beim useCartStore(selector) Aufruf uebergeben werden. Solange der Selector nur ein primitives Feld liest, reicht die eingebaute Referenzgleichheit von Zustand vollstaendig aus, ganz ohne zusaetzliche Bibliothek.
Fuer komplexere abgeleitete Werte, die aus mehreren Feldern berechnet werden, empfiehlt sich weiterhin eine leichte Memoization mit useMemo im aufrufenden Component oder eine dedizierte Selector Funktion mit useShallow aus dem Zustand Paket, das flache Objektvergleiche fuer Selectors ermoeglicht, die mehrere Felder gleichzeitig zurueckgeben. Wer von Redux zu Zustand migriert und Reselect Selectors hatte, kann die reine Berechnungslogik meist unveraendert uebernehmen und nur den Memoization Mechanismus austauschen.
5. Async Actions: von Thunks zu einfachen Funktionen
Redux Thunks kapseln asynchrone Logik in Funktionen, die statt eines Action Objekts eine Funktion zurueckgeben, die dispatch und getState als Parameter erhaelt. Bei der Migration von Redux zu Zustand entfaellt dieser Umweg komplett, weil eine Zustand Action einfach eine normale asynchrone Funktion sein kann, die set direkt nach dem Await aufruft.
// BEFORE: Redux thunk for an async action
export const fetchProducts = () => async (dispatch, getState) => {
dispatch({ type: 'products/loading' });
try {
const products = await api.getProducts();
dispatch({ type: 'products/loaded', payload: products });
} catch (error) {
dispatch({ type: 'products/error', payload: error.message });
}
};
// AFTER: Zustand store with the async logic inlined as a plain function
export const useProductStore = create((set) => ({
products: [],
isLoading: false,
error: null,
fetchProducts: async () => {
set({ isLoading: true, error: null });
try {
const products = await api.getProducts();
set({ products, isLoading: false });
} catch (error) {
set({ error: error.message, isLoading: false });
}
},
}));
6. Middleware und Persistenz nachbilden
Custom Redux Middleware fuer Logging, Analytics oder Persistenz muss bei der Migration nicht verloren gehen. Zustand bietet eigene Middleware Funktionen wie persist fuer LocalStorage Persistenz und subscribeWithSelector fuer gezielte Reaktionen auf State Aenderungen ausserhalb von React Komponenten, etwa fuer Analytics Events. Beide lassen sich wie Redux Middleware um den Store herum kombinieren.
Ein Team, das von Redux zu Zustand migriert und bisher redux-persist fuer die Synchronisation mit LocalStorage genutzt hat, ersetzt das durch die persist Middleware von Zustand, die deutlich weniger Konfiguration braucht und ohne separate Rehydration Logik in der App auskommt. Fuer sehr spezifische Middleware ohne direktes Zustand Aequivalent laesst sich fast immer eine eigene, wenige Zeilen lange Wrapper Funktion um die Store Definition schreiben.
7. Devtools und Time Travel Debugging behalten
Ein Argument, das gegen die Migration von Redux zu Zustand ins Feld gefuehrt wird, ist der Verlust der Redux Devtools mit Time Travel Debugging. Das stimmt so nicht mehr: Zustand bietet ueber die devtools Middleware dieselbe Browser Extension Integration, inklusive Action Namen, Diff Ansicht und der Moeglichkeit, zu einem frueheren State zurueckzuspringen.
Der einzige Unterschied ist, dass jede State Aenderungsfunktion in Zustand einen Namen fuer die Devtools bekommen sollte, etwa set(newState, false, 'cart/addItem'), damit die Devtools Anzeige aehnlich sprechend bleibt wie bei benannten Redux Actions. Teams, die von Redux zu Zustand migrieren, sollten diese Namenskonvention von Anfang an konsequent einhalten, um den Debugging Komfort nicht zu verlieren.
8. Redux und Zustand parallel betreiben
Waehrend der Uebergangsphase muessen beide State Container koexistieren koennen, ohne dass Komponenten wissen, welchen sie gerade lesen. Ein pragmatischer Ansatz ist ein Facade Hook pro fachlichem Bereich, der intern entweder useSelector oder den neuen Zustand Store anspricht, je nachdem, ob der Slice schon migriert wurde. Ein Feature Flag pro Slice macht sichtbar, welcher Teil der App bereits umgestellt ist.
Wichtig ist, dass beide Systeme wirklich unabhaengig bleiben und nicht versuchen, sich gegenseitig zu synchronisieren. Ein Redux Slice, der Werte aus einem Zustand Store liest, um sie in seinen eigenen State zu kopieren, erzeugt zwei Wahrheiten fuer denselben Wert und damit genau die Bugs, die eine saubere Migration eigentlich vermeiden soll.
9. Redux und Zustand im direkten Vergleich
Die folgende Tabelle fasst die wichtigsten Unterschiede zusammen, die bei der Entscheidung, von Redux zu Zustand zu migrieren, tatsaechlich den Ausschlag geben.
| Aspekt | Redux Toolkit | Zustand | Auswirkung |
|---|---|---|---|
| Boilerplate pro Feature | Slice, Reducer, Selector | Ein Store Objekt | Deutlich weniger Dateien |
| Bundle Groesse | Groesser mit React Redux | Sehr klein, kein Provider | Schnellere Initial Loads |
| Async Logik | Thunks oder RTK Query | Normale async Funktionen | Weniger Konzepte lernen |
| Devtools | Nativ eingebaut | Ueber devtools Middleware | Beide gleichwertig nutzbar |
| Grosse Teams, viele Slices | Etablierte Konventionen | Konventionen selbst aufsetzen | Redux vorteilhaft bei sehr grossen Teams |
Die Tabelle zeigt, dass die Migration von Redux zu Zustand nicht in jedem Fall die richtige Entscheidung ist. Sehr grosse Teams mit vielen Entwicklern profitieren von den etablierten, erzwungenen Konventionen von Redux, waehrend kleinere Teams und Projekte mit strengen Bundle Groessen Limits meist deutlich von Zustand profitieren.
Mironsoft
React State Management, Redux Migrationen und Architekturberatung
Redux Boilerplate stoert die Produktivitaet eures Teams?
Wir analysieren euren Redux Store, planen die Migration Slice fuer Slice und begleiten den Umstieg auf Zustand inklusive Middleware, Devtools und Testabdeckung.
Store Audit
Slice Analyse nach Abhaengigkeiten und Migrationsaufwand
Begleitete Migration
Koexistenz von Redux und Zustand ohne Regressionen
Middleware Uebertrag
Persistenz, Logging und Devtools Konventionen sauber uebernehmen
10. Zusammenfassung
Wer von Redux zu Zustand migrieren will, sollte niemals in einem Big Bang starten, sondern Slice fuer Slice vorgehen und mit fachlichen Bereichen beginnen, die wenige Abhaengigkeiten zu anderen Teilen des Stores haben. Reducer, Action Creator und Selector werden durch ein einziges Store Objekt ersetzt, das State und Update Funktionen kombiniert. Thunks weichen normalen asynchronen Funktionen, Middleware fuer Persistenz und Devtools bleibt ueber Zustand eigene Erweiterungen erhalten.
Der groesste Gewinn der Migration von Redux zu Zustand ist selten die reine Performance, sondern die drastisch reduzierte Menge an Boilerplate und die kuerzere Einarbeitungszeit fuer neue Teammitglieder. Fuer sehr grosse Teams mit strikten Konventionsanforderungen bleibt Redux dennoch in vielen Faellen die robustere Wahl, weshalb die Entscheidung fuer die Migration projektspezifisch getroffen werden sollte.
Von Redux zu Zustand migrieren: Das Wichtigste auf einen Blick
Migrationsreihenfolge
Slice fuer Slice, beginnend mit fachlichen Bereichen ohne viele Cross Slice Abhaengigkeiten.
Store Struktur
Reducer, Actions und Selectors werden zu einem einzigen Store Objekt mit set und get Funktionen.
Middleware
persist und devtools Middleware von Zustand decken die meisten Redux Middleware Anwendungsfaelle ab.
Koexistenz
Facade Hooks pro Slice halten die Migration fuer aufrufende Komponenten unsichtbar.