import { create } from 'zustand';
interface WizardState {
step: number;
formData: Record<string, string>;
setField: (field: string, value: string) => void;
nextStep: () => void;
reset: () => void;
}
const initialData = { step: 1, formData: {} };
// Store-ul Zustand e decoupled complet de React components
export const useWizardStore = create<WizardState>((set) => ({
...initialData,
setField: (field, value) =>
set((state) => ({
formData: { ...state.formData, [field]: value }
})),
nextStep: () => set((state) => ({ step: state.step + 1 })),
reset: () => set(initialData)
}));Anul trecut am refăcut un wizard de onboarding cu 6 pași și vreo 40 de câmpuri pentru un client din fintech. Am început cu useReducer fiindcă eram în mania "nu adăugăm lib-uri externe dacă avem React nativ". În două săptămâni ne-am trezit cu o supă de dispatch-uri pasate prin 5 niveluri de componente și re-renderări obositoare.
Când e useReducer exact ce trebuie
Să fim clari: useReducer nu e un tool rău. Este genial pentru stare locală complexă, undeva unde ai tranziții de stare strict legate între ele și totul trăiește într-un singur sub-arbore de componente. Dacă faci un player video custom, o filtru complex de căutare într-un tabel sau un editor de texte cu opțiune de undo/redo, un reducer local e sfânt.
Câștigi izolare totală. Când componenta tată se demontează, starea dispare curat din memorie fără să-ți bați capul cu curățarea manuală sau cu resetStore().
Problema apare când încerci să combini useReducer cu React.Context ca să scapi de prop drilling. La fiecare dispatch, toate componentele consumatoare din Context se re-randează, chiar dacă le pasă doar de un singur boolean din tot obiectul tău de stare.
Unde schimbă Zustand jocul
La proiectul menționat, după ce performanța pe telefoane mai slabe scăzuse sub 30 FPS în timpul tastării, am aruncat Context-ul și am băgat Zustand. Am redus codul de stare cu peste 30% și am tăiat re-renderările inutile instant.
Marele avantaj la Zustand nu e doar că e mic (are sub 2kB), ci cum funcționează selecția atomică. Componenta ta se abonează doar la felia de stare de care are nevoie via selectori (state => state.step).
În plus, ai acces la stare în afara arborelui de React. Am putut să citesc un token din store direct dintr-un interceptor de Axios sau dintr-un script de analytics, fără să fac giumbușlucuri cu ref-uri sau event-uri pe window.
Trade-off-uri pe care nu le vezi în tutoriale
Nimic nu e moca în software engineering și ambele variante au bile negre.
Zustand e prin definiție un store extern. Am pățit o chestie urâtă pe producție: un utilizator completa jumătate din wizard, naviga înapoi la dashboard și apoi intra din nou în checkout. Pentru că Zustand păstrează starea în memorie globală, câmpurile erau completate cu datele de la sesiunea anterioară. A trebuit să adăugăm manual un reset pe unmount. La useReducer chestia asta era imposibilă pentru că starea murea odată cu componenta.
De asemenea, useReducer te obligă la o structură mai riguroasă dacă lucrezi într-o echipă mare cu mulți juniori. La Zustand e foarte ușor ca cineva să scrie setter-e haotice direct în componente dacă nu ești atent la code review.
Concluzie și regula mea de aur
Eu aplic o regulă simplă acum:
- Starea e strict locală și moare odată cu ecranul respectiv? Folosesc
useReducer(sau chiaruseStatedacă e simplă). - Starea e împărțită între componente izolate, am nevoie de selecție fină la render sau acces din API layer? Zustand direct.
Voi ce folosiți pe proiectele curente? Mai folosește cineva Redux Toolkit în 2026 sau am trecut toți pe Zustand și TanStack Query?