zustand-5
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen to Use
适用场景
Triggers: When managing global state in React, using Zustand, or implementing state slices.
Load when: managing global or shared state in React, implementing persistence, using Zustand stores, or needing out-of-component state access.
触发场景:在React中管理全局状态、使用Zustand或实现状态切片时。
适用时机:在React中管理全局或共享状态、实现持久化功能、使用Zustand存储,或需要在组件外部访问状态时。
Critical Patterns
核心模式
Pattern 1: Basic typed store
模式1:基础类型化存储
typescript
import { create } from 'zustand';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
export const useCounterStore = create<CounterState>()((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));typescript
import { create } from 'zustand';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
export const useCounterStore = create<CounterState>()((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));Pattern 2: Selectors to avoid unnecessary re-renders
模式2:使用选择器避免不必要的重新渲染
typescript
// ❌ Selects the entire store — re-renders on ANY change
const store = useCounterStore();
// ✅ Select only what you need
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
// ✅ Multiple values with useShallow
import { useShallow } from 'zustand/react/shallow';
const { count, increment } = useCounterStore(
useShallow((state) => ({ count: state.count, increment: state.increment }))
);typescript
// ❌ 选择整个存储 — 任何变化都会触发重新渲染
const store = useCounterStore();
// ✅ 仅选择所需内容
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
// ✅ 使用useShallow选择多个值
import { useShallow } from 'zustand/react/shallow';
const { count, increment } = useCounterStore(
useShallow((state) => ({ count: state.count, increment: state.increment }))
);Pattern 3: Persistence
模式3:持久化
typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface SettingsState {
theme: 'light' | 'dark';
language: string;
setTheme: (theme: 'light' | 'dark') => void;
setLanguage: (lang: string) => void;
}
export const useSettingsStore = create<SettingsState>()(
persist(
(set) => ({
theme: 'light',
language: 'es',
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
}),
{ name: 'settings-storage' } // localStorage key
)
);typescript
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface SettingsState {
theme: 'light' | 'dark';
language: string;
setTheme: (theme: 'light' | 'dark') => void;
setLanguage: (lang: string) => void;
}
export const useSettingsStore = create<SettingsState>()(
persist(
(set) => ({
theme: 'light',
language: 'es',
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
}),
{ name: 'settings-storage' } // localStorage 键名
)
);Code Examples
代码示例
Store with async and loading/error states
包含异步逻辑及加载/错误状态的存储
typescript
interface UserState {
users: User[];
isLoading: boolean;
error: string | null;
fetchUsers: () => Promise<void>;
createUser: (data: CreateUserInput) => Promise<void>;
}
export const useUserStore = create<UserState>()((set) => ({
users: [],
isLoading: false,
error: null,
fetchUsers: async () => {
set({ isLoading: true, error: null });
try {
const users = await api.getUsers();
set({ users, isLoading: false });
} catch (error) {
set({ error: String(error), isLoading: false });
}
},
createUser: async (data) => {
set({ isLoading: true });
try {
const user = await api.createUser(data);
set((state) => ({ users: [...state.users, user], isLoading: false }));
} catch (error) {
set({ error: String(error), isLoading: false });
}
},
}));typescript
interface UserState {
users: User[];
isLoading: boolean;
error: string | null;
fetchUsers: () => Promise<void>;
createUser: (data: CreateUserInput) => Promise<void>;
}
export const useUserStore = create<UserState>()((set) => ({
users: [],
isLoading: false,
error: null,
fetchUsers: async () => {
set({ isLoading: true, error: null });
try {
const users = await api.getUsers();
set({ users, isLoading: false });
} catch (error) {
set({ error: String(error), isLoading: false });
}
},
createUser: async (data) => {
set({ isLoading: true });
try {
const user = await api.createUser(data);
set((state) => ({ users: [...state.users, user], isLoading: false }));
} catch (error) {
set({ error: String(error), isLoading: false });
}
},
}));Slices pattern (modular)
切片模式(模块化)
typescript
// userSlice.ts
interface UserSlice {
user: User | null;
setUser: (user: User) => void;
clearUser: () => void;
}
const createUserSlice = (set: any): UserSlice => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
});
// cartSlice.ts
interface CartSlice {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
}
const createCartSlice = (set: any): CartSlice => ({
items: [],
addItem: (item) => set((state: any) => ({ items: [...state.items, item] })),
removeItem: (id) => set((state: any) => ({
items: state.items.filter((i: CartItem) => i.id !== id)
})),
});
// store.ts — combine slices
export const useStore = create<UserSlice & CartSlice>()((...args) => ({
...createUserSlice(...args),
...createCartSlice(...args),
}));typescript
// userSlice.ts
interface UserSlice {
user: User | null;
setUser: (user: User) => void;
clearUser: () => void;
}
const createUserSlice = (set: any): UserSlice => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
});
// cartSlice.ts
interface CartSlice {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
}
const createCartSlice = (set: any): CartSlice => ({
items: [],
addItem: (item) => set((state: any) => ({ items: [...state.items, item] })),
removeItem: (id) => set((state: any) => ({
items: state.items.filter((i: CartItem) => i.id !== id)
})),
});
// store.ts — 合并切片
export const useStore = create<UserSlice & CartSlice>()((...args) => ({
...createUserSlice(...args),
...createCartSlice(...args),
}));Immer for direct mutations
使用Immer实现直接修改
typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
interface TodoState {
todos: { id: string; text: string; done: boolean }[];
toggleTodo: (id: string) => void;
}
export const useTodoStore = create<TodoState>()(
immer((set) => ({
todos: [],
toggleTodo: (id) => set((state) => {
const todo = state.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done; // Direct mutation OK with Immer
}),
}))
);typescript
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
interface TodoState {
todos: { id: string; text: string; done: boolean }[];
toggleTodo: (id: string) => void;
}
export const useTodoStore = create<TodoState>()(
immer((set) => ({
todos: [],
toggleTodo: (id) => set((state) => {
const todo = state.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done; // 使用Immer时可直接修改
}),
}))
);DevTools + external access
DevTools + 外部访问
typescript
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
export const useAppStore = create<AppState>()(
devtools(
(set) => ({ /* ... */ }),
{ name: 'AppStore' } // Name in Redux DevTools
)
);
// Access outside of components
const state = useAppStore.getState();
const unsubscribe = useAppStore.subscribe(
(state) => state.user,
(user) => console.log('User changed:', user)
);typescript
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
export const useAppStore = create<AppState>()(
devtools(
(set) => ({ /* ... */ }),
{ name: 'AppStore' } // Redux DevTools中的名称
)
);
// 在组件外部访问状态
const state = useAppStore.getState();
const unsubscribe = useAppStore.subscribe(
(state) => state.user,
(user) => console.log('User changed:', user)
);Anti-Patterns
反模式
❌ Selecting the entire store
❌ 选择整个存储
typescript
// ❌ Re-renders on any store change
const { count, user, settings, items } = useStore();
// ✅ Select only what you need
const count = useStore((s) => s.count);typescript
// ❌ 存储的任何变化都会触发重新渲染
const { count, user, settings, items } = useStore();
// ✅ 仅选择所需内容
const count = useStore((s) => s.count);❌ Async logic directly in set
❌ 在set中直接编写异步逻辑
typescript
// ❌ Don't put async inside set
set(async (state) => { /* ... */ });
// ✅ Use get() or define the async in the action
fetchData: async () => {
const data = await api.get();
set({ data });
}typescript
// ❌ 不要在set内部放置异步逻辑
set(async (state) => { /* ... */ });
// ✅ 使用get()或在action中定义异步逻辑
fetchData: async () => {
const data = await api.get();
set({ data });
}Quick Reference
快速参考
| Task | Pattern |
|---|---|
| Basic store | |
| Simple selector | |
| Multiple fields | |
| Persistence | |
| Mutations | |
| DevTools | |
| External access | |
| Subscription | |
| 任务 | 模式 |
|---|---|
| 基础存储 | |
| 简单选择器 | |
| 多个字段 | |
| 持久化 | |
| 状态修改 | |
| DevTools | |
| 外部访问 | |
| 订阅 | |
Rules
规则
- Stores must be split by domain concern (auth store, cart store, UI store) — a single global store that grows without bound is a maintenance anti-pattern
- Always use selectors to subscribe to specific state slices () — subscribing to the full store object causes unnecessary re-renders on any state change
useStore(s => s.count) - Persist middleware () must be applied only to stores that genuinely need persistence; over-persisting creates stale-state bugs after schema changes
zustand/middleware - Store actions must be defined inside the callback, not as external functions that receive the store as a parameter
create - Zustand 5 uses for object selectors to prevent re-renders when returned object references change — wrap object selectors with
useShallowuseShallow
- 存储必须按业务领域拆分(认证存储、购物车存储、UI存储)—— 无限制膨胀的单一全局存储是维护反模式
- 始终使用选择器订阅特定状态切片()—— 订阅完整存储对象会导致任何状态变化都触发不必要的重新渲染
useStore(s => s.count) - 持久化中间件()仅应应用于真正需要持久化的存储;过度持久化会在 schema 变更后导致 stale-state 错误
zustand/middleware - 存储动作必须定义在回调内部,而不是作为接收存储作为参数的外部函数
create - Zustand 5 使用处理对象选择器,以避免返回对象引用变化时触发重新渲染—— 用
useShallow包裹对象选择器useShallow