zustand-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseZustand Patterns
Zustand 状态管理模式
Modern state management with Zustand 5.x - lightweight, TypeScript-first, no boilerplate.
基于Zustand 5.x的现代状态管理——轻量、TypeScript优先、无冗余模板代码。
Overview
概述
- Global state without Redux complexity
- Shared state across components without prop drilling
- Persisted state with localStorage/sessionStorage
- Computed/derived state with selectors
- State that needs middleware (logging, devtools, persistence)
- 无需Redux复杂度的全局状态管理
- 无需属性透传即可在组件间共享状态
- 借助localStorage/sessionStorage实现状态持久化
- 通过选择器实现计算/派生状态
- 支持中间件的状态(日志、调试工具、持久化等)
Core Patterns
核心模式
1. Basic Store with TypeScript
1. 基于TypeScript的基础状态仓库
typescript
import { create } from 'zustand';
interface BearState {
bears: number;
increase: (by: number) => void;
reset: () => void;
}
const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
reset: () => set({ bears: 0 }),
}));typescript
import { create } from 'zustand';
interface BearState {
bears: number;
increase: (by: number) => void;
reset: () => void;
}
const useBearStore = create<BearState>()((set) => ({
bears: 0,
increase: (by) => set((state) => ({ bears: state.bears + by })),
reset: () => set({ bears: 0 }),
}));2. Slices Pattern (Modular Stores)
2. 切片模式(模块化状态仓库)
typescript
import { create, StateCreator } from 'zustand';
// Auth slice
interface AuthSlice {
user: User | null;
login: (user: User) => void;
logout: () => void;
}
const createAuthSlice: StateCreator<AuthSlice & CartSlice, [], [], AuthSlice> = (set) => ({
user: null,
login: (user) => set({ user }),
logout: () => set({ user: null }),
});
// Cart slice
interface CartSlice {
items: CartItem[];
addItem: (item: CartItem) => void;
clearCart: () => void;
}
const createCartSlice: StateCreator<AuthSlice & CartSlice, [], [], CartSlice> = (set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
clearCart: () => set({ items: [] }),
});
// Combined store
const useStore = create<AuthSlice & CartSlice>()((...a) => ({
...createAuthSlice(...a),
...createCartSlice(...a),
}));typescript
import { create, StateCreator } from 'zustand';
// Auth 切片
interface AuthSlice {
user: User | null;
login: (user: User) => void;
logout: () => void;
}
const createAuthSlice: StateCreator<AuthSlice & CartSlice, [], [], AuthSlice> = (set) => ({
user: null,
login: (user) => set({ user }),
logout: () => set({ user: null }),
});
// 购物车切片
interface CartSlice {
items: CartItem[];
addItem: (item: CartItem) => void;
clearCart: () => void;
}
const createCartSlice: StateCreator<AuthSlice & CartSlice, [], [], CartSlice> = (set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
clearCart: () => set({ items: [] }),
});
// 组合后的状态仓库
const useStore = create<AuthSlice & CartSlice>()((...a) => ({
...createAuthSlice(...a),
...createCartSlice(...a),
}));3. Immer Middleware (Immutable Updates)
3. Immer 中间件(不可变更新)
typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
interface TodoState {
todos: Todo[];
addTodo: (text: string) => void;
toggleTodo: (id: string) => void;
updateNested: (id: string, subtaskId: string, done: boolean) => void;
}
const useTodoStore = create<TodoState>()(
immer((set) => ({
todos: [],
addTodo: (text) =>
set((state) => {
state.todos.push({ id: crypto.randomUUID(), text, done: false });
}),
toggleTodo: (id) =>
set((state) => {
const todo = state.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done;
}),
updateNested: (id, subtaskId, done) =>
set((state) => {
const todo = state.todos.find((t) => t.id === id);
const subtask = todo?.subtasks?.find((s) => s.id === subtaskId);
if (subtask) subtask.done = done;
}),
}))
);typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
interface TodoState {
todos: Todo[];
addTodo: (text: string) => void;
toggleTodo: (id: string) => void;
updateNested: (id: string, subtaskId: string, done: boolean) => void;
}
const useTodoStore = create<TodoState>()(
immer((set) => ({
todos: [],
addTodo: (text) =>
set((state) => {
state.todos.push({ id: crypto.randomUUID(), text, done: false });
}),
toggleTodo: (id) =>
set((state) => {
const todo = state.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done;
}),
updateNested: (id, subtaskId, done) =>
set((state) => {
const todo = state.todos.find((t) => t.id === id);
const subtask = todo?.subtasks?.find((s) => s.id === subtaskId);
if (subtask) subtask.done = done;
}),
}))
);4. Persist Middleware
4. 持久化中间件
typescript
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
interface SettingsState {
theme: 'light' | 'dark';
language: string;
setTheme: (theme: 'light' | 'dark') => void;
}
const useSettingsStore = create<SettingsState>()(
persist(
(set) => ({
theme: 'light',
language: 'en',
setTheme: (theme) => set({ theme }),
}),
{
name: 'settings-storage',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ theme: state.theme }), // Only persist theme
version: 1,
migrate: (persisted, version) => {
if (version === 0) {
// Migration logic
}
return persisted as SettingsState;
},
}
)
);typescript
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
interface SettingsState {
theme: 'light' | 'dark';
language: string;
setTheme: (theme: 'light' | 'dark') => void;
}
const useSettingsStore = create<SettingsState>()(
persist(
(set) => ({
theme: 'light',
language: 'en',
setTheme: (theme) => set({ theme }),
}),
{
name: 'settings-storage',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ theme: state.theme }), // 仅持久化主题
version: 1,
migrate: (persisted, version) => {
if (version === 0) {
// 迁移逻辑
}
return persisted as SettingsState;
},
}
)
);5. Selectors (Prevent Re-renders)
5. 选择器(避免不必要重渲染)
typescript
// ❌ BAD: Re-renders on ANY state change
const { bears, fish } = useBearStore();
// ✅ GOOD: Only re-renders when bears changes
const bears = useBearStore((state) => state.bears);
// ✅ GOOD: Shallow comparison for objects (Zustand 5.x)
import { useShallow } from 'zustand/react/shallow';
const { bears, fish } = useBearStore(
useShallow((state) => ({ bears: state.bears, fish: state.fish }))
);
// ✅ GOOD: Computed/derived state via selector
const totalAnimals = useBearStore((state) => state.bears + state.fish);
// ❌ BAD: Storing computed state
const useStore = create((set) => ({
items: [],
total: 0, // Don't store derived values!
addItem: (item) => set((s) => ({
items: [...s.items, item],
total: s.total + item.price, // Sync issues!
})),
}));
// ✅ GOOD: Compute in selector
const total = useStore((s) => s.items.reduce((sum, i) => sum + i.price, 0));typescript
// ❌ 错误:任何状态变化都会触发重渲染
const { bears, fish } = useBearStore();
// ✅ 正确:仅当bears变化时触发重渲染
const bears = useBearStore((state) => state.bears);
// ✅ 正确:对对象进行浅比较(Zustand 5.x)
import { useShallow } from 'zustand/react/shallow';
const { bears, fish } = useBearStore(
useShallow((state) => ({ bears: state.bears, fish: state.fish }))
);
// ✅ 正确:通过选择器实现计算/派生状态
const totalAnimals = useBearStore((state) => state.bears + state.fish);
// ❌ 错误:存储计算后的状态
const useStore = create((set) => ({
items: [],
total: 0, // 不要存储派生值!
addItem: (item) => set((s) => ({
items: [...s.items, item],
total: s.total + item.price, // 会出现同步问题!
})),
}));
// ✅ 正确:在选择器中计算
const total = useStore((s) => s.items.reduce((sum, i) => sum + i.price, 0));6. Async Actions
6. 异步操作
typescript
interface UserState {
user: User | null;
loading: boolean;
error: string | null;
fetchUser: (id: string) => Promise<void>;
}
const useUserStore = create<UserState>()((set) => ({
user: null,
loading: false,
error: null,
fetchUser: async (id) => {
set({ loading: true, error: null });
try {
const user = await api.getUser(id);
set({ user, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));typescript
interface UserState {
user: User | null;
loading: boolean;
error: string | null;
fetchUser: (id: string) => Promise<void>;
}
const useUserStore = create<UserState>()((set) => ({
user: null,
loading: false,
error: null,
fetchUser: async (id) => {
set({ loading: true, error: null });
try {
const user = await api.getUser(id);
set({ user, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},
}));7. DevTools Integration
7. 调试工具集成
typescript
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
const useStore = create<State>()(
devtools(
(set) => ({
// ... state and actions
}),
{ name: 'MyStore', enabled: process.env.NODE_ENV === 'development' }
)
);typescript
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
const useStore = create<State>()(
devtools(
(set) => ({
// ... 状态与操作
}),
{ name: 'MyStore', enabled: process.env.NODE_ENV === 'development' }
)
);Quick Reference
快速参考
typescript
// ✅ Create typed store with double-call pattern
const useStore = create<State>()((set, get) => ({ ... }));
// ✅ Use selectors for all state access
const count = useStore((s) => s.count);
// ✅ Use useShallow for multiple values (Zustand 5.x)
const { a, b } = useStore(useShallow((s) => ({ a: s.a, b: s.b })));
// ✅ Middleware order: immer → subscribeWithSelector → devtools → persist
create(persist(devtools(immer((set) => ({ ... })))))
// ❌ Never destructure entire store
const store = useStore(); // Re-renders on ANY change
// ❌ Never store server state (use TanStack Query instead)
const useStore = create((set) => ({ users: [], fetchUsers: async () => ... }));typescript
// ✅ 使用双调用模式创建类型化状态仓库
const useStore = create<State>()((set, get) => ({ ... }));
// ✅ 所有状态访问都使用选择器
const count = useStore((s) => s.count);
// ✅ 使用useShallow获取多个值(Zustand 5.x)
const { a, b } = useStore(useShallow((s) => ({ a: s.a, b: s.b })));
// ✅ 中间件顺序:immer → subscribeWithSelector → devtools → persist
create(persist(devtools(immer((set) => ({ ... })))))
// ❌ 绝不要解构整个状态仓库
const store = useStore(); // 任何状态变化都会触发重渲染
// ❌ 绝不要存储服务端状态(改用TanStack Query)
const useStore = create((set) => ({ users: [], fetchUsers: async () => ... }));Key Decisions
关键决策
| Decision | Option A | Option B | Recommendation |
|---|---|---|---|
| State structure | Single store | Multiple stores | Slices in single store - easier cross-slice access |
| Nested updates | Spread operator | Immer middleware | Immer for deeply nested state (3+ levels) |
| Persistence | Manual localStorage | persist middleware | persist middleware with partialize |
| Multiple values | Multiple selectors | useShallow | useShallow for 2-5 related values |
| Server state | Zustand | TanStack Query | TanStack Query - Zustand for client-only state |
| DevTools | Always on | Conditional | Conditional - |
| 决策项 | 选项A | 选项B | 推荐方案 |
|---|---|---|---|
| 状态结构 | 单一仓库 | 多仓库 | 单一仓库内使用切片 - 跨切片访问更便捷 |
| 嵌套更新 | 展开运算符 | Immer中间件 | Immer - 适用于深度嵌套状态(3层及以上) |
| 状态持久化 | 手动localStorage | persist中间件 | persist中间件 - 配合partialize使用 |
| 多值获取 | 多个选择器 | useShallow | useShallow - 适用于2-5个关联值 |
| 服务端状态 | Zustand | TanStack Query | TanStack Query - Zustand仅用于客户端状态 |
| 调试工具 | 始终开启 | 条件开启 | 条件开启 - |
Anti-Patterns (FORBIDDEN)
反模式(禁止使用)
typescript
// ❌ FORBIDDEN: Destructuring entire store
const { count, increment } = useStore(); // Re-renders on ANY state change
// ❌ FORBIDDEN: Storing derived/computed state
const useStore = create((set) => ({
items: [],
total: 0, // Will get out of sync!
}));
// ❌ FORBIDDEN: Storing server state
const useStore = create((set) => ({
users: [], // Use TanStack Query instead
fetchUsers: async () => { ... },
}));
// ❌ FORBIDDEN: Mutating state without Immer
set((state) => {
state.items.push(item); // Breaks reactivity!
return state;
});
// ❌ FORBIDDEN: Using deprecated shallow import
import { shallow } from 'zustand/shallow'; // Use useShallow from zustand/react/shallowtypescript
// ❌ 禁止:解构整个状态仓库
const { count, increment } = useStore(); // 任何状态变化都会触发重渲染
// ❌ 禁止:存储派生/计算状态
const useStore = create((set) => ({
items: [],
total: 0, // 会出现不同步问题!
}));
// ❌ 禁止:存储服务端状态
const useStore = create((set) => ({
users: [], // 改用TanStack Query
fetchUsers: async () => { ... },
}));
// ❌ 禁止:不使用Immer直接修改状态
set((state) => {
state.items.push(item); // 会破坏响应式!
return state;
});
// ❌ 禁止:使用已废弃的shallow导入
import { shallow } from 'zustand/shallow'; // 改用zustand/react/shallow中的useShallowIntegration with React Query
与React Query的集成
typescript
// ✅ Zustand for CLIENT state (UI, preferences, local-only)
const useUIStore = create<UIState>()((set) => ({
sidebarOpen: false,
theme: 'light',
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));
// ✅ TanStack Query for SERVER state (API data)
function Dashboard() {
const sidebarOpen = useUIStore((s) => s.sidebarOpen);
const { data: users } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
// Zustand: UI state | TanStack Query: server data
}typescript
// ✅ Zustand用于客户端状态(UI、偏好设置、本地专属状态)
const useUIStore = create<UIState>()((set) => ({
sidebarOpen: false,
theme: 'light',
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}));
// ✅ TanStack Query用于服务端状态(API数据)
function Dashboard() {
const sidebarOpen = useUIStore((s) => s.sidebarOpen);
const { data: users } = useQuery({ queryKey: ['users'], queryFn: fetchUsers });
// Zustand:UI状态 | TanStack Query:服务端数据
}Related Skills
相关技能
- - Server state management (use with Zustand for client state)
tanstack-query-advanced - - Form state (React Hook Form vs Zustand for forms)
form-state-patterns - - RSC hydration considerations with Zustand
react-server-components-framework
- - 服务端状态管理(与Zustand配合用于客户端状态)
tanstack-query-advanced - - 表单状态(React Hook Form vs Zustand用于表单)
form-state-patterns - - 服务端组件与Zustand的 hydration 注意事项
react-server-components-framework
Capability Details
能力细节
store-creation
store-creation
Keywords: zustand, create, store, typescript, state
Solves: Setting up type-safe Zustand stores with proper TypeScript inference
关键词: zustand, create, store, typescript, state
解决问题: 搭建具备TypeScript类型推导的类型安全Zustand状态仓库
slices-pattern
slices-pattern
Keywords: slices, modular, split, combine, StateCreator
Solves: Organizing large stores into maintainable, domain-specific slices
关键词: slices, modular, split, combine, StateCreator
解决问题: 将大型状态仓库拆分为可维护的、领域专属的切片
middleware-stack
middleware-stack
Keywords: immer, persist, devtools, middleware, compose
Solves: Combining middleware in correct order for immutability, persistence, and debugging
关键词: immer, persist, devtools, middleware, compose
解决问题: 按正确顺序组合中间件,实现不可变更新、持久化与调试
selector-optimization
selector-optimization
Keywords: selector, useShallow, re-render, performance, memoization
Solves: Preventing unnecessary re-renders with proper selector patterns
关键词: selector, useShallow, re-render, performance, memoization
解决问题: 通过正确的选择器模式避免不必要的重渲染
persistence-migration
persistence-migration
Keywords: persist, localStorage, sessionStorage, migrate, version
Solves: Persisting state with schema migrations between versions
关键词: persist, localStorage, sessionStorage, migrate, version
解决问题: 实现状态持久化及版本间的 schema 迁移
References
参考资料
- - Combining multiple middleware
references/middleware-composition.md - - Production-ready store template
scripts/store-template.ts - - Implementation checklist
checklists/zustand-checklist.md
- - 多中间件组合方式
references/middleware-composition.md - - 生产环境可用的状态仓库模板
scripts/store-template.ts - - 实现检查清单
checklists/zustand-checklist.md