Zustand
Минималистичный глобальный стор: create, set со слиянием, селекторы, persist.
Обновлено
Что такое Zustand
Zustand — маленькая (~1 КБ) библиотека для глобального состояния: стор создаётся функцией и используется как хук. Без провайдера, без строковых типов действий, без редьюсеров — только состояние и функции, которые его меняют.
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) // селектор
const add = useCart(state => state.add)
<button onClick={() => add(product.id)}>Добавить</button>Как это работает
createвозвращает хук. Стор живёт вне React; компоненты на него подписываются.setсливает (merge) новый объект с текущим состоянием на первом уровне — копировать всё не нужно.- Компонент перерисовывается, только когда меняется значение, которое возвращает селектор.
Селекторы — самая важная деталь
| Код | Перерисовка, когда |
|---|---|
useCart() (без селектора) |
меняется что угодно в сторе ✗ |
useCart(s => s.items.length) |
только когда меняется количество ✓ |
useCart(s => ({ a: s.a, b: s.b })) |
всегда — новый объект на каждом рендере ✗ |
useCart(useShallow(s => ({ a: s.a, b: s.b }))) |
когда меняется a или b ✓ |
Правило: выбирайте как можно меньше; для нескольких значений — несколько вызовов или useShallow.
Полезные middleware
import { persist, devtools } from 'zustand/middleware'
export const useSettings = create<Settings>()(
persist(
set => ({ theme: 'light', setTheme: theme => set({ theme }) }),
{ name: 'settings' }, // ключ в localStorage
),
)| Middleware | Что делает |
|---|---|
persist |
сохраняет стор в localStorage / sessionStorage |
devtools |
подключает Redux DevTools |
immer |
«мутабельный» синтаксис, как в RTK |
Zustand или Redux Toolkit
| Zustand | Redux Toolkit | |
|---|---|---|
| Код для простого стора | ~10 строк | слайс + стор + провайдер + типизированные хуки |
| Провайдер | не нужен | нужен |
| Навязанная структура | мало — решаете сами | много — слайсы, действия |
| Серверное состояние | нет (используйте TanStack Query) | RTK Query в комплекте |
| Подходит для | большинства приложений | больших приложений, команд, которым нужны строгие правила |
Zustand с Next.js
Глобальный стор на уровне модуля на сервере общий для всех запросов. Правила:
- Используйте стор только в Client Components.
- Не кладите туда данные конкретного пользователя при серверном рендере; для этого создавайте стор на запрос (
createStore+ Context) — паттерн описан в официальном гайде. - С
persistзначение изlocalStorageпоявляется только после гидратации.
Где он лежит в FSD
Стор, связанный с сущностью → entities/cart/model/cart-store.ts. Глобальный UI-стор → shared/model/ или слайс, которому он принадлежит.
Коротко
- Zustand =
create→ хук;setсливает; без провайдера. - Маленькие селекторы;
useShallowдля нескольких значений. persistдля localStorage; в Next — только на клиенте, осторожно с данными конкретного пользователя.