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

useReducer sau Zustand? Când merită să complici lucrurile și când e mai bine să fii leneș

De Diana Oprea, 23 mai 2026 · 7 vizualizări · 3 like-uri

Postat 23 mai 2026
typescript
import { create } from 'zustand';

interface FilterState {
  search: string;
  tags: string[];
  setSearch: (text: string) => void;
}

// Store-ul este extern, nu provoacă re-render-uri globale aiurea
export const useFilterStore = create<FilterState>((set) => ({
  search: '',
  tags: [],
  setSearch: (text) => set({ search: text }),
}));

// În componentă, randarea se face DOAR când se schimbă proprietatea selectată
const SearchInput = () => {
  const search = useFilterStore((state) => state.search);
  const setSearch = useFilterStore((state) => state.setSearch);
  
  return <input value={search} onChange={e => setSearch(e.target.value)} />;
};

Am trecut recent printr-o refactorizare masivă la un dashboard de management unde aveam vreo 15 taburi dinamice și date destul de interconectate. Inițial, echipa pornise pe ideea „facem totul nativ, fără librării externe”. Sună bine pe hârtie, nu? Am ajuns să avem un monstru format din useReducer și React.Context care re-randa jumătate de pagină la fiecare apăsare de tastă.

Atunci am zis „stop” și am trecut o parte din stare în Zustand. Am economisit cam 30% din timpul de dezvoltare pentru ecranele noi și am scăpat de un boilerplate infect. Dar asta nu înseamnă că useReducer e inutil.

Când rămâi la useReducer (și când devine periculos)

useReducer este excelent pentru starea complexă, dar locală. Dacă ai un formular gigant, un wizard cu 4 pași sau o componentă de tabel cu sortare, filtrare și paginare proprie, useReducer e sfânt. Ține logica de update grupată într-un singur loc (reducerul) și nu poluează restul aplicației.

Problema apare când încerci să transformi useReducer în store global împachetându-l într-un Context.Provider.

Am pățit-o la un proiect cu vreo 8k useri activi. Când contextul se updatează, toate componentele care consumă acel context se re-pavează (re-render), chiar dacă ele folosesc doar o bucățică mică din stare. Sigur, poți să spargi în mai multe contexte sau să folosești useMemo până îți sângerează ochii, dar de ce să te chinui? Aici intervine trade-off-ul: vrei neapărat „zero dependencies” cu prețul performanței și al nervilor tăi?

De ce Zustand e pur și simplu mai sănătos

Zustand rezolvă fix buba asta fără să te oblige să scrii sute de linii de cod ca în Redux Toolkit. Are un model mental extrem de simplu și folosește selectoare pentru a preveni re-randările inutile.

Spre deosebire de Context, în Zustand te abonezi doar la ce ai nevoie. Dacă se schimbă doar user.name, componenta ta de avatar se va re-randa, dar meniul de navigare nu. Totul se întâmplă în afara arborelui de React, ceea ce înseamnă că performanța e brici din start.

Trade-off-ul sincer

Zustand e genial pentru că e flexibil, dar fix flexibilitatea asta e și cel mai mare risc al lui. E extrem de ușor să transformi un store de Zustand într-o „gropă de gunoi” globală. Am văzut proiecte unde programatorii puneau starea de open/closed a unui amărât de dropdown în store-ul global Zustand doar pentru că le era lene să scrie un useState local.

Dacă pui totul la comun, pierzi încapsularea. Când o componentă e distrusă (unmount), starea ei din Zustand rămâne acolo, pe când la useReducer se curăță automat odată cu componenta.

Regula mea de aur

Eu aplic o regulă simplă acum:

  1. Starea e folosită doar de o componentă și copiii ei direcți? Folosesc useState sau useReducer local.
  2. Starea trebuie accesată de componente din ramuri complet diferite ale aplicației (ex: user session, temă, coș de cumpărături)? Merg direct pe Zustand.

Tu cum faci? Te mai chinui cu Context pentru stări globale sau ai trecut deja pe Zustand/Jotai?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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