03-状态管理与路由——05. Zustand 状态更新

📅 2026/6/16 11:53:56
03-状态管理与路由——05. Zustand 状态更新
05. Zustand 状态更新一、5W1H 概述维度内容What使用 set 函数更新状态支持 Immer 简化不可变更新Why确保状态更新的不可变性When需要修改 store 中的状态时Wherestore 的 action 函数中Who使用 Zustand 的开发者Howset((state) ({ count: state.count 1 }))二、What - 什么是 set 函数Zustand 中的set函数是更新 store 状态的唯一方式。它接收一个更新函数或新状态对象并自动处理不可变更新。const useStore create((set) ({ count: 0, // 使用函数式更新 increment: () set((state) ({ count: state.count 1 })), // 直接设置新值 reset: () set({ count: 0 }) }));三、Why - 为什么需要这种方式3.1 不可变性React 依赖不可变性来检测状态变化。Zustand 的 set 函数确保状态以不可变方式更新。3.2 可预测性使用 set 更新状态的方式统一且可预测。四、When - 何时使用哪种更新方式场景推荐方式示例依赖当前状态函数式更新set(state ({ count: state.count 1 }))不依赖当前状态直接设置set({ loading: false })更新嵌套对象函数式更新 展开set(state ({ user: { ...state.user, name: newName } }))数组操作函数式更新set(state ({ items: [...state.items, newItem] }))五、Where - 在哪里使用store 的 action 函数内部组件中可以通过getState()和setState()直接使用// 在 action 中使用 const useStore create((set) ({ count: 0, increment: () set((state) ({ count: state.count 1 })) })); // 在组件中直接使用 function Component() { const setCount useStore(state state.setCount); // 或直接调用 useStore.setState() }六、Who - 谁需要使用所有使用 Zustand 的开发者都需要掌握状态更新方法。七、How - 如何更新状态7.1 基础更新import { create } from zustand; const useStore create((set) ({ // 简单类型 count: 0, name: , isOpen: false, // 函数式更新依赖当前状态 increment: () set((state) ({ count: state.count 1 })), decrement: () set((state) ({ count: state.count - 1 })), // 直接设置不依赖当前状态 reset: () set({ count: 0 }), setName: (name) set({ name }), toggle: () set((state) ({ isOpen: !state.isOpen })) }));7.2 对象更新const useUserStore create((set) ({ user: { name: 张三, age: 25, email: zhangexample.com }, // 更新单个字段 setName: (name) set((state) ({ user: { ...state.user, name } })), // 更新多个字段 updateUser: (updates) set((state) ({ user: { ...state.user, ...updates } })), // 重置用户 resetUser: () set({ user: { name: , age: 0, email: } }) }));7.3 数组更新const useTodoStore create((set) ({ todos: [], // 添加元素 addTodo: (text) set((state) ({ todos: [...state.todos, { id: Date.now(), text, completed: false }] })), // 删除元素 removeTodo: (id) set((state) ({ todos: state.todos.filter(todo todo.id ! id) })), // 更新元素 toggleTodo: (id) set((state) ({ todos: state.todos.map(todo todo.id id ? { ...todo, completed: !todo.completed } : todo ) })), // 清空数组 clearTodos: () set({ todos: [] }), // 排序 sortTodos: () set((state) ({ todos: [...state.todos].sort((a, b) a.text.localeCompare(b.text)) })) }));7.4 嵌套对象更新const useStore create((set) ({ data: { user: { profile: { name: 张三, age: 25 }, settings: { theme: light, notifications: true } } }, // 更新嵌套属性 updateProfileName: (name) set((state) ({ data: { ...state.data, user: { ...state.data.user, profile: { ...state.data.user.profile, name } } } })), // 更简洁的方式使用 Immer updateProfileAge: (age) set((state) { state.data.user.profile.age age; return state; }) }));7.5 使用 Immer 简化更新npminstallimmerimport { create } from zustand; import { produce } from immer; const useStore create((set) ({ user: { name: 张三, age: 25, address: { city: 北京, street: 长安街 } }, todos: [], // 使用 immer 简化嵌套更新 updateCity: (city) set( produce((state) { state.user.address.city city; }) ), // 数组操作也更简单 addTodo: (text) set( produce((state) { state.todos.push({ id: Date.now(), text, completed: false }); }) ), // 复杂更新 complexUpdate: () set( produce((state) { state.user.age 1; state.user.address.city 上海; state.todos state.todos.filter(t !t.completed); }) ) }));7.6 批量更新const useStore create((set) ({ count: 0, name: , loading: false, // 一次 set 更新多个状态 resetAll: () set({ count: 0, name: , loading: false }), // 基于当前状态批量更新 startLoading: () set((state) ({ loading: true, count: state.count 1 })), // 条件更新 conditionalUpdate: (condition) set((state) ({ count: condition ? state.count 1 : state.count - 1, loading: false })) }));7.7 异步更新const useUserStore create((set, get) ({ user: null, loading: false, error: null, fetchUser: async (id) { // 开始加载 set({ loading: true, error: null }); try { const response await fetch(/api/users/${id}); const user await response.json(); // 成功 set({ user, loading: false }); } catch (error) { // 失败 set({ error: error.message, loading: false }); } }, // 使用 get 获取当前状态 updateAndLog: (newCount) { set({ count: newCount }); const currentCount get().count; console.log(当前计数:, currentCount); } }));7.8 乐观更新const useTodoStore create((set, get) ({ todos: [], addTodoOptimistic: async (text) { const tempId Date.now(); const newTodo { id: tempId, text, completed: false, isPending: true }; // 乐观更新 set((state) ({ todos: [...state.todos, newTodo] })); try { const response await fetch(/api/todos, { method: POST, body: JSON.stringify({ text }) }); const savedTodo await response.json(); // 替换临时 todo set((state) ({ todos: state.todos.map(todo todo.id tempId ? { ...savedTodo, isPending: false } : todo ) })); } catch (error) { // 回滚 set((state) ({ todos: state.todos.filter(todo todo.id ! tempId) })); } } }));7.9 条件更新const useStore create((set) ({ count: 0, maxCount: 10, increment: () set((state) { if (state.count state.maxCount) { console.warn(已达到最大值); return state; // 不更新 } return { count: state.count 1 }; }), decrement: () set((state) { if (state.count 0) { console.warn(已达到最小值); return state; } return { count: state.count - 1 }; }) }));八、常见陷阱8.1 直接修改状态// ❌ 错误直接修改 const useStore create((set) ({ user: { name: 张三 }, updateName: (name) { const state get(); state.user.name name; // 直接修改 set(state); } })); // ✅ 正确创建新对象 const useStore create((set) ({ user: { name: 张三 }, updateName: (name) set((state) ({ user: { ...state.user, name } })) }));8.2 忘记使用函数式更新// ❌ 可能导致过期状态 const useStore create((set) ({ count: 0, incrementTwice: () { set({ count: count 1 }); set({ count: count 1 }); // 只增加 1 } })); // ✅ 使用函数式更新 const useStore create((set) ({ count: 0, incrementTwice: () { set((state) ({ count: state.count 1 })); set((state) ({ count: state.count 1 })); } }));九、练习题创建一个计数器 Store实现 1、-1、重置创建一个 Todo Store实现添加、删除、切换完成创建一个购物车 Store实现添加商品、修改数量、删除商品十、小结要点说明函数式更新set((state) ({ ... }))直接设置set({ ... })对象更新使用展开运算符数组更新使用扩展运算符或 filter/mapImmer简化嵌套更新