Actions 与状态更新 #
什么是 Actions? #
在 Zustand 中,Actions 是用于更新状态的函数。与 Redux 不同,Zustand 的 Actions 直接定义在 store 中,无需额外的 action creators 或 reducers。
基本结构 #
tsx
interface StoreState {
// 状态
count: number
// Actions
increment: () => void
decrement: () => void
reset: () => void
}
const useStore = create<StoreState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}))
定义 Actions #
简单 Actions #
tsx
const useStore = create((set) => ({
name: '',
// 直接设置
setName: (name: string) => set({ name }),
// 清空
clearName: () => set({ name: '' }),
}))
带参数的 Actions #
tsx
interface Todo {
id: string
text: string
completed: boolean
}
const useTodoStore = create<TodoState>((set) => ({
todos: [],
// 添加 todo
addTodo: (text: string) => set((state) => ({
todos: [
...state.todos,
{ id: Date.now().toString(), text, completed: false }
]
})),
// 切换完成状态
toggleTodo: (id: string) => set((state) => ({
todos: state.todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
})),
// 删除 todo
removeTodo: (id: string) => set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id)
})),
}))
使用 get 函数 #
tsx
const useStore = create((set, get) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
// 使用 get 获取当前状态
doubleAndIncrement: () => {
const currentCount = get().count
set({ count: currentCount * 2 + 1 })
},
// 异步操作中使用 get
asyncIncrement: async () => {
const currentCount = get().count
await new Promise((resolve) => setTimeout(resolve, 1000))
set({ count: currentCount + 1 })
},
}))
不可变更新 #
Zustand 遵循 React 的不可变数据原则。更新状态时,必须创建新的对象/数组,而不是直接修改。
对象更新 #
tsx
interface User {
name: string
email: string
settings: {
theme: 'light' | 'dark'
notifications: boolean
}
}
const useUserStore = create<{ user: User; updateUser: (data: Partial<User>) => void }>((set) => ({
user: {
name: 'John',
email: 'john@example.com',
settings: {
theme: 'light',
notifications: true,
},
},
// 更新顶层属性
updateUser: (data) => set((state) => ({
user: { ...state.user, ...data }
})),
// 更新嵌套属性
updateTheme: (theme) => set((state) => ({
user: {
...state.user,
settings: {
...state.user.settings,
theme,
},
},
})),
}))
数组更新 #
tsx
interface Item {
id: string
name: string
}
const useListStore = create((set) => ({
items: [] as Item[],
// 添加到末尾
addItem: (item: Item) => set((state) => ({
items: [...state.items, item]
})),
// 添加到开头
prependItem: (item: Item) => set((state) => ({
items: [item, ...state.items]
})),
// 删除
removeItem: (id: string) => set((state) => ({
items: state.items.filter((item) => item.id !== id)
})),
// 更新
updateItem: (id: string, data: Partial<Item>) => set((state) => ({
items: state.items.map((item) =>
item.id === id ? { ...item, ...data } : item
)
})),
// 排序
sortItems: () => set((state) => ({
items: [...state.items].sort((a, b) => a.name.localeCompare(b.name))
})),
// 反转
reverseItems: () => set((state) => ({
items: [...state.items].reverse()
})),
}))
使用 Immer 简化更新 #
对于复杂的嵌套状态,可以使用 Immer 中间件:
tsx
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'
const useStore = create(
immer((set) => ({
user: {
name: 'John',
settings: {
theme: 'light',
notifications: {
email: true,
push: false,
},
},
},
// 使用 Immer,可以直接"修改"状态
updateTheme: (theme: 'light' | 'dark') => set((state) => {
state.user.settings.theme = theme
}),
updateNotification: (type: 'email' | 'push', value: boolean) => set((state) => {
state.user.settings.notifications[type] = value
}),
}))
)
批量更新 #
单次更新多个状态 #
tsx
const useStore = create((set) => ({
count: 0,
name: '',
isLoading: false,
// 批量更新(只触发一次重渲染)
initialize: () => set({
count: 0,
name: 'Initial',
isLoading: false,
}),
// 基于前一个状态批量更新
reset: () => set((state) => ({
count: 0,
name: '',
isLoading: false,
})),
}))
批量操作 #
tsx
const useCartStore = create((set) => ({
items: [] as CartItem[],
total: 0,
count: 0,
// 添加多个商品(一次更新)
addMultipleItems: (newItems: CartItem[]) => set((state) => {
const items = [...state.items, ...newItems]
return {
items,
total: items.reduce((sum, item) => sum + item.price, 0),
count: items.length,
}
}),
}))
复杂状态操作 #
条件更新 #
tsx
const useCounterStore = create((set, get) => ({
count: 0,
max: 100,
min: 0,
increment: () => {
const { count, max } = get()
if (count < max) {
set({ count: count + 1 })
}
},
decrement: () => {
const { count, min } = get()
if (count > min) {
set({ count: count - 1 })
}
},
setCount: (value: number) => {
const { max, min } = get()
const clampedValue = Math.min(max, Math.max(min, value))
set({ count: clampedValue })
},
}))
链式更新 #
tsx
const useGameStore = create((set, get) => ({
score: 0,
level: 1,
lives: 3,
addScore: (points: number) => {
const { score, level } = get()
const newScore = score + points
// 检查是否升级
if (newScore >= level * 100) {
set({ score: newScore, level: level + 1 })
} else {
set({ score: newScore })
}
},
loseLife: () => {
const { lives } = get()
if (lives <= 1) {
// 游戏结束
set({ lives: 0, score: 0, level: 1 })
} else {
set({ lives: lives - 1 })
}
},
}))
事务性更新 #
tsx
const useTransferStore = create((set, get) => ({
accounts: {
alice: 1000,
bob: 500,
},
transfer: (from: string, to: string, amount: number) => {
const { accounts } = get()
// 验证
if (accounts[from] < amount) {
throw new Error('Insufficient funds')
}
// 原子更新
set({
accounts: {
...accounts,
[from]: accounts[from] - amount,
[to]: (accounts[to] || 0) + amount,
},
})
},
}))
Action 模式 #
Action 分离模式 #
tsx
// 定义状态
interface State {
count: number
}
// 定义 Actions
interface Actions {
increment: () => void
decrement: () => void
reset: () => void
}
type StoreState = State & Actions
const initialState: State = {
count: 0,
}
const createActions = (set: any, get: any): Actions => ({
increment: () => set((state: State) => ({ count: state.count + 1 })),
decrement: () => set((state: State) => ({ count: state.count - 1 })),
reset: () => set(initialState),
})
const useStore = create<StoreState>((set, get) => ({
...initialState,
...createActions(set, get),
}))
Action 复用模式 #
tsx
// 通用 CRUD actions
function createCrudActions<T extends { id: string }>(set: any, get: any, key: string) {
return {
[`add${key}`]: (item: T) => set((state: any) => ({
[key.toLowerCase()]: [...state[key.toLowerCase()], item]
})),
[`remove${key}`]: (id: string) => set((state: any) => ({
[key.toLowerCase()]: state[key.toLowerCase()].filter((item: T) => item.id !== id)
})),
[`update${key}`]: (id: string, data: Partial<T>) => set((state: any) => ({
[key.toLowerCase()]: state[key.toLowerCase()].map((item: T) =>
item.id === id ? { ...item, ...data } : item
)
})),
}
}
// 使用
const useTodoStore = create((set, get) => ({
todos: [],
...createCrudActions<Todo>(set, get, 'Todos'),
}))
实际案例 #
表单状态管理 #
tsx
interface FormData {
username: string
email: string
password: string
}
interface FormState {
data: FormData
errors: Partial<Record<keyof FormData, string>>
touched: Partial<Record<keyof FormData, boolean>>
isSubmitting: boolean
setField: (field: keyof FormData, value: string) => void
setTouched: (field: keyof FormData) => void
validate: () => boolean
submit: () => Promise<void>
reset: () => void
}
const useFormStore = create<FormState>((set, get) => ({
data: {
username: '',
email: '',
password: '',
},
errors: {},
touched: {},
isSubmitting: false,
setField: (field, value) => set((state) => ({
data: { ...state.data, [field]: value }
})),
setTouched: (field) => set((state) => ({
touched: { ...state.touched, [field]: true }
})),
validate: () => {
const { data } = get()
const errors: Partial<Record<keyof FormData, string>> = {}
if (!data.username) errors.username = '用户名必填'
if (!data.email) errors.email = '邮箱必填'
if (!data.password) errors.password = '密码必填'
set({ errors })
return Object.keys(errors).length === 0
},
submit: async () => {
const { validate, data } = get()
if (!validate()) return
set({ isSubmitting: true })
try {
await fetch('/api/register', {
method: 'POST',
body: JSON.stringify(data),
})
set({ isSubmitting: false })
} catch (error) {
set({ isSubmitting: false })
}
},
reset: () => set({
data: { username: '', email: '', password: '' },
errors: {},
touched: {},
isSubmitting: false,
}),
}))
最佳实践 #
1. Action 命名规范 #
tsx
// ✅ 好的命名
setUser: (user) => set({ user })
clearUser: () => set({ user: null })
updateUserName: (name) => set((state) => ({ user: { ...state.user, name } }))
// ❌ 不好的命名
update: (data) => set(data)
change: (value) => set({ value })
2. 保持 Actions 简单 #
tsx
// ✅ 好:单一职责
increment: () => set((state) => ({ count: state.count + 1 }))
// ❌ 不好:混合逻辑
increment: () => {
set((state) => ({ count: state.count + 1 }))
console.log('incremented')
analytics.track('increment')
}
3. 使用 TypeScript 类型 #
tsx
// ✅ 好:完整类型定义
interface Actions {
setUser: (user: User) => void
updateUser: (id: string, data: Partial<User>) => void
}
总结 #
Actions 是 Zustand 状态更新的核心:
- Actions 直接定义在 store 中
- 使用
set函数更新状态 - 使用
get函数读取当前状态 - 遵循不可变更新原则
- 可以使用 Immer 简化复杂更新
- 支持批量更新和条件更新
接下来,让我们学习 异步操作,处理异步数据获取。
最后更新:2026-03-28