eduardweb.
Hooks & PatternsIntermediar#state-management#zustand#react#javascript#use-reducer

useReducer vs Zustand: Când chiar ai nevoie de Redux-like state și când te complici degeaba

De Ana Ionescu, 12 aug. 2026 · 2 vizualizări · 2 like-uri

Postat acum 4 zile
typescript
// 1. Local state complex cu useReducer
type WizardState = { step: number; data: Record<string, string>; isValid: boolean };
type WizardAction = { type: 'NEXT_STEP'; payload: Record<string, string> } | { type: 'RESET' };

function wizardReducer(state: WizardState, action: WizardAction): WizardState {
  switch (action.type) {
    case 'NEXT_STEP':
      return { ...state, step: state.step + 1, data: { ...state.data, ...action.payload } };
    case 'RESET':
      return { step: 1, data: {}, isValid: false };
    default:
      return state;
  }
}

// 2. Zustand store pentru stare globală cu selecție atomică
import { create } from 'zustand';

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

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

M-am lovit recent de un refactoring masiv pe un proiect de e-commerce cu vreo 12k utilizatori activi zilnic. Echipa inițială pusese absolut totul în Context API combinat cu useReducer, de la datele din coșul de cumpărături până la valoarea fiecărui input dintr-un formular de checkout. Rezultatul? Re-renderări agresive la fiecare apăsare de tastă și o latență vizibilă pe telefoane mai slabe.

Am eliminat tot wrapper-ul de context și am separat clar responsabilitățile: stare locală cu useReducer acolo unde aveam logică complexă de pas cu pas, și Zustand pentru datele globale. Am economisit cam 30% din timpul de render pe paginile critice și am șters peste 400 de linii de boilerplate inutil.

Când useReducer e exact ce-ți trebuie

useReducer nu este un înlocuitor ieftin de Redux și nici un mod de a gestiona stare globală. Este un instrument nativ excelent pentru stare locală complexă.

Când ai o componentă ale cărei modificări de stare depind strâns unele de altele, useState devine rapid un dezastru de 8-10 setSomething apelate consecutiv. Cu useReducer, centralizezi tranzițiile și previi stările invalide.

Am avut cazul unui wizard de onboarding în 5 pași. Validările depindeau direct de ce bifase userul la pasul anterior. Folosind useReducer:

  • Am încapsulat toată logica de tranziție într-o funcție pură, izolată de UI.
  • Am eliminat complet bug-urile unde starea râmânea desincronizată.
  • Am putut scrie teste unitare direct pe reducer, fără să randezi vreo componentă React.

Buba apare când încerci să pasezi dispatch-ul și state-ul prin React.createContext spre zeci de componente copii. În momentul ăla, orice componentă care consumă contextul se va re-randa la orice modificare din reducer, indiferent dacă folosește proprietatea schimbată sau nu.

Zustand: Simplitate fără re-renders nejustificate

Zustand funcționează în afara React Render Tree-ului. Asta schimbă complet ecuația pentru că scapi de două mari dureri de cap:

  1. Nu mai ai nevoie de <Provider>-e imbricate la rădăcina aplicației care îți poluează arborele de componente.
  2. Folosești selectori fini (de exemplu, useStore(s => s.user)), iar componenta se va re-randa STRICT când se schimbă valoarea extrasă, nu tot obiectul.

Asta te scapă de hacks cu useMemo sau React.memo împrăștiate peste tot prin cod. Plus că poți accesa sau modifica starea Zustand direct în afara React — de exemplu într-un interceptor de Axios sau într-un middleware de WebSocket.

Cât despre trade-off? Fiind un store global flexibil și ușor de creat, dacă ai juniori în echipă, există riscul masiv să transforme Zustand într-un "dumpster" în care aruncă orice stare, inclusiv chestii care trebuiau să rămână 100% locale într-un input.

Regula mea de aur

După mai mulți ani de luptă cu state management-ul în React, aplic o regulă simplă:

  • useState pentru toggle-uri, modal-uri simple și input-uri nelegate de alte stări.
  • useReducer când am 3+ stări interdependente izolate într-o singură pagină sau flux (wizard, configurator).
  • Zustand doar când datele trebuie partajate între rute diferite sau componente aflate la niveluri complet diferite în DOM (autentificare, coș, setări de temă, notificări).

Voi unde trageți linia între starea locală și cea globală? Mai folosește cineva useReducer + Context pe proiecte noi sau ați trecut direct la Zustand sau Jotai?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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