Zustand
Store global minimalist: create, set cu merge, selectori, persist.
Actualizat
Ce este Zustand
Zustand e o librărie mică (~1 KB) pentru state global: un store creat cu o funcție, folosit ca un hook. Fără provider, fără acțiuni de tip string, fără reduceri — doar state și funcții care îl modifică.
import { create } from 'zustand'
type CartState = {
items: number[]
add: (id: number) => void
clear: () => void
}
export const useCart = create<CartState>()(set => ({
items: [],
add: id => set(state => ({ items: [...state.items, id] })),
clear: () => set({ items: [] }),
}))'use client'
const count = useCart(state => state.items.length) // selector
const add = useCart(state => state.add)
<button onClick={() => add(product.id)}>Adaugă</button>Cum funcționează
createreturnează un hook. Store-ul trăiește în afara React-ului; componentele se abonează la el.setcombină (merge) obiectul nou cu state-ul existent, pe primul nivel — nu trebuie să copiezi tot.- Componenta se re-randează doar când valoarea întoarsă de selector se schimbă.
Selectorii — cel mai important detaliu
| Cod | Re-randare când |
|---|---|
useCart() (fără selector) |
orice se schimbă în store ✗ |
useCart(s => s.items.length) |
doar când se schimbă numărul ✓ |
useCart(s => ({ a: s.a, b: s.b })) |
mereu — obiect nou la fiecare randare ✗ |
useCart(useShallow(s => ({ a: s.a, b: s.b }))) |
când a sau b se schimbă ✓ |
Regula: selectează cât mai puțin; pentru mai multe valori, mai multe apeluri sau useShallow.
Middleware utile
import { persist, devtools } from 'zustand/middleware'
export const useSettings = create<Settings>()(
persist(
set => ({ theme: 'light', setTheme: theme => set({ theme }) }),
{ name: 'settings' }, // cheia din localStorage
),
)| Middleware | Ce face |
|---|---|
persist |
salvează store-ul în localStorage / sessionStorage |
devtools |
conectează la Redux DevTools |
immer |
sintaxă „mutabilă”, ca în RTK |
Zustand vs Redux Toolkit
| Zustand | Redux Toolkit | |
|---|---|---|
| Cod pentru un store simplu | ~10 linii | slice + store + provider + hook-uri tipate |
| Provider | nu e nevoie | da |
| Structură impusă | puțină — tu decizi | multă — slice-uri, acțiuni |
| Server state | nu (folosește TanStack Query) | RTK Query inclus |
| Potrivit | majoritatea aplicațiilor | aplicații mari, echipe care vor reguli stricte |
Zustand cu Next.js
Un store global de modul e partajat pe server între request-uri. Reguli:
- Folosește store-ul doar în Client Components.
- Nu pune în el date specifice unui user la randarea pe server; pentru asta, creează store-ul per request (
createStore+ Context) — ghidul oficial descrie pattern-ul. - Pentru
persist, valoarea dinlocalStorageapare abia după hidratare.
Unde stă în FSD
Store legat de o entitate → entities/cart/model/cart-store.ts. Store de UI global → shared/model/ sau slice-ul care îl deține.
Pe scurt
- Zustand =
create→ hook;setface merge; fără provider. - Selectori mici;
useShallowpentru mai multe valori. persistpentru localStorage; în Next doar pe client, atenție la date per user.