import { create } from 'zustand';
type CheckoutState = {
step: number;
promoCode: string;
isProcessing: boolean;
setPromoCode: (code: string) => void;
nextStep: () => void;
reset: () => void;
};
const initialState = {
step: 1,
promoCode: '',
isProcessing: false,
};
// Store atomic - componentele se abonează DOAR la felia de stare necesară
export const useCheckoutStore = create<CheckoutState>((set) => ({
...initialState,
setPromoCode: (promoCode) => set({ promoCode }),
nextStep: () => set((state) => ({ step: state.step + 1 })),
reset: () => set(initialState),
}));
// Exemplu de utilizare în componentă fără re-renderări inutile:
// Componenta asta se va re-rendera DOAR când se schimbă step, ignoră promoCode!
export function StepIndicator() {
const step = useCheckoutStore((state) => state.step);
return <div>Pasul curent: {step}</div>;
}Am văzut prea multe aplicații React blocate în două extreme: fie se aruncă Zustand peste tot, chiar și pentru un toggle de modal, fie se chinuie lumea cu Context și useReducer prop-drilled 5 niveluri în jos. Ambele abordări creează datori tehnice pe care le plătești scump mai târziu. Hai să lămurim unde tragem linia ca să scriem cod curat fără să ne complicăm viața.
De ce am renunțat la useReducer pe un flux complex de checkout
Anul trecut lucram la o aplicație de e-commerce cu vreo 12k useri pe zi. Am zis să fim „puriști” și să nu adăugăm librării externe pentru starea din procesul de checkout (trei pași, date de facturare, selecție de locuri și cupoane). Am mers pe useReducer combinat cu React Context.
La început a fost ok. Dup-aia au apărut cerințele reale: când schimbi adresa de livrare, trebuie să revalidezi stocul și să recalculezi taxele în timp ce userul tastează. Pentru că starea din Context se schimba la fiecare keystroke, tot arborele de componente din checkout se re-rendera. Am băgat useMemo și useCallback până când codul arăta ca un tablou de bord din anii '80.
Am pierdut o zi întreagă optimizând re-renderări, doar ca să ne dăm seama că spărgeam Contextul în 4 provideri separați ca să izolăm starea. O nebunie curată.
Când Zustand îți salvează sănătatea mentală
Am refactorizat tot checkout-ul respectiv pe Zustand în vreo 3 ore și am șters vreo 180 de linii de cod inutil. Cel mai mare câștig n-a fost sintaxa mai scurtă, ci selectorii atomici.
Zustand trăiește în afara React-ului. O componentă se re-renderează doar dacă valoarea exactă pe care o ceri prin selector s-a schimbat. Dintr-odată, câmpul de „Cod Poștal” nu mai făcea re-render la componenta de „Sumar Comandă” până când nu se schimba efectiv prețul total.
Câștigul masiv cu Zustand:
- Fără Provider Hell la nivel de aplicație.
- Selectori nativi ultra-rapizi fără
React.memoforțat peste tot. - Poți citi sau modifica starea și în afara componentelor React (de exemplu, într-un interceptor de Axios/Fetch).
Marea problemă cu Zustand (și trade-off-ul sincer)
Nu totul e roz. Starea din Zustand e globală și persistența ei prin memoria RAM poate deveni un cosmar dacă nu ești atent.
Am pățit-o la un dashboard unde aveam un modal de editare date. Am pus starea form-ului în Zustand. Userul deschidea modalul, completa jumătate, închidea modalul. Când îl deschidea din nou pentru alt client, vedea datele vechi pentru că uitasem să apelez o funcție de reset() pe unmount. Starea locală din useState sau useReducer se curăță automat când componenta dispare din DOM. Cu Zustand, trebuie să gestionezi manual curățenia dacă folosești store-ul pentru chestii temporare.
Regula mea după 10+ ani de dezvoltare
Să nu uităm că useReducer este excelent, dar are un caz de utilizare foarte specific: stare complexă dar localizată.
Dacă ai o componentă complexă (de exemplu, un Rich Text Editor, un widget de Calendar custom sau un canvas interactiov) unde ai 5-6 acțiuni interdependente (SET_SELECTION, DRAG_START, UNDO), useReducer e sfânt. Îți ține toată logica într-un singur loc, izolat complet în acea componentă, fără nicio dependență externă.
Cum decid eu azi:
- Componentă izolată cu logică stufoasă? ->
useReducer(fără să trec starea prin Context mai mult de 1-2 niveluri). - Stare partajată între rute, module sau componente îndepărtate? ->
Zustanddirect. - Formulare? -> Niciuna. Folosesc
react-hook-formsautanstack-formdirect, nu inventez roata.
Când ați folosit ultima dată useReducer pe producție fără să vă pară rău două săptămâni mai târziu?