eduardweb.
Hooks & PatternsIntermediar#state-management#zustand#react#frontend

useReducer vs Zustand: Când merită starea locală complexă și când treci la store global

De Liliana Ghiță, 27 iul. 2026 · 5 vizualizări · 2 like-uri

Postat 27 iul. 2026
typescript
import { create } from 'zustand';

type CartState = {
  items: string[];
  addItem: (item: string) => void;
  clearCart: () => void;
};

export const useCartStore = create<CartState>((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
  clearCart: () => set({ items: [] }),
}));

// Consum eficient prin selector: re-randare DOAR când se schimbă length
export const CartCount = () => {
  const count = useCartStore((state) => state.items.length);
  return <span>Produse în coș: {count}</span>;
};

Am văzut prea multe aplicații React stricate din două motive opuse: fie băgai Zustand sau Redux pentru absolut orice toggle de UI, fie încercai să pasezi dispatch-ul dintr-un useReducer prin 6 niveluri de componente până îți venea să te lași de meserie.

Anul trecut am refăcut checkout-ul pentru o platformă de e-commerce cu vreo 15k useri activi lunar. Inițial, tot wizard-ul de cumpărare (adrese, cupoane, payment gateway, validări) era aruncat într-un store global de Zustand. Arăta curat la prima vedere, dar după două luni aveam bug-uri ciudate de race conditions când userul naviga înapoi și starea veche nu se curăța corect.

Când useReducer e exact ce-ți trebuie

Mulați-vă pe regula asta simplă: dacă starea trăiește și moare odată cu ecranul sau widget-ul respectiv, e stare locală. Punct. Nu are ce căuta într-un store global.

Când ai un formular complex cu mulți pași sau o componentă de tip Data Grid unde acțiunile depind unele de altele (de exemplu: "dacă bifezi X, resetează Y și calculează Z"), useReducer e sfânt. Menține logica de business izolată și ușor de testat unitar fără să randezi componente.

Avantajul uriaș la useReducer este că se curăță singur când componenta se demontează (unmount). Nu trebuie să-ți aduci aminte să dai resetStore() pe useEffect cleanup.

Unde crapă useReducer (și intră Zustand)

Problema apare în secunda în care trebuie să împarți starea aia cu o sidebar din altă ramură a arborelui de React. Sau când ajungi la "prop drilling hell" și începi să înfășori totul în React Context.

Știm cu toții ce se întâmplă cu React Context: la fiecare update de stare, toate componentele consumatoare se rerandează dacă nu ești maniac cu useMemo și useCallback. Aici am economisit vreo 30% la build time pe re-render-uri când am migrat o bucată de dashboard de pe Context + useReducer pe Zustand.

Zustand îți dă un store atomic, în afara arborelui de React. Cel mai mare plus? Selectors. Componenta X ascultă doar de state.user.name. Dacă se schimbă state.user.theme, Componenta X nici nu clipește.

Trade-off-uri reale: Ce am învățat în producție

  • Zustand e ieftin, dar periculos la memorie: Dacă nu resetezi starea când userul dă logout sau părăsește o pagină complexă, rămâi cu mizerii în memorie. Am avut un bug unde un coș de cumpărături fantomă persista între sesiuni din cauza asta.
  • useReducer cere mai mult boilerplate: Reducerul pur te obligă să scrii switch-uri și action types. Dacă ai doar 2-3 câmpuri, un simplu useState e mai mult decât suficient. Nu deveniți arhitecți over-engineered fără rost.
  • Persistență vs Ephemere: Ai nevoie de localStorage sync? Zustand are middleware dedicat (persist) pe care îl configurezi în 3 linii. Să faci asta manual cu useReducer și useEffect e o pierdere de timp.

Voi cum abordați starea într-un feature nou? Plecați din start cu Zustand sau începeți cu useState/useReducer și le promovați la store global doar când vă doare prop drilling-ul?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.