如何使用 Zustand 创建和管理全局状态?
核心答案
Zustand 通过 create 函数创建 store,将状态和操作定义在同一对象中,组件通过 hook 选择性订阅所需状态片段,从而实现轻量级的全局状态管理。与 Redux 相比,Zustand 无需 Provider 包裹、无需 reducer/action 分离,store 可在组件外直接调用 getState()/setState(),且内置 selector 机制避免不必要的重渲染。
创建 Store
typescriptimport { create } from 'zustand'; interface CounterState { count: number; increment: () => void; decrement: () => void; reset: () => void; } 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 }), }));
set 接受对象或函数:对象形式直接替换字段,函数形式接收当前 state 返回增量更新。两种方式都是浅合并,不会覆盖未提及的字段。
组件中使用
typescriptfunction Counter() { // 选择性订阅:仅 count 变化时重渲染 const count = useCounterStore((state) => state.count); const increment = useCounterStore((state) => state.increment); return ( <div> <p>{count}</p> <button onClick={increment}>+1</button> </div> ); }
选择订阅是 Zustand 性能优势的核心:只有 selector 返回值变化时组件才会重渲染。直接解构 const { count } = useCounterStore() 会导致整个 store 任一字段变化都触发渲染。
异步操作与中间件
typescriptimport { create } from 'zustand'; import { devtools, persist } from 'zustand/middleware'; const useUserStore = create( devtools( persist( (set) => ({ user: null, fetchUser: async (id: string) => { const res = await fetch(`/api/users/${id}`); const user = await res.json(); set({ user }, false, { type: 'fetchUser' }); }, logout: () => set({ user: null }, false, { type: 'logout' }), }), { name: 'user-storage' } // localStorage 持久化 ) ) );
异步操作无需额外处理,在 action 中直接 await 后调用 set 即可。persist 将状态持久化到 storage,devtools 接入 Redux DevTools 调试。中间件通过函数组合嵌套,顺序影响行为。
组件外访问 Store
typescript// 读取当前状态 const currentUser = useUserStore.getState().user; // 监听状态变化 const unsub = useUserStore.subscribe((state) => { console.log('user changed:', state.user); }); // 直接更新状态 useUserStore.setState({ user: null });
这一特性使得在工具函数、路由守卫、API 拦截器等非组件场景中也能读写全局状态,是 Zustand 区别于 Context API 的重要优势。
追问
-
Zustand 的
set是浅合并还是替换?能否实现深度合并? 默认浅合并;深度合并需手动展开{ ...state.nested, ...newVal }或搭配immer中间件。 -
如何拆分大型 Store? 可以按领域创建多个独立 store(如
useUserStore、useCartStore),也可以用slices模式将逻辑拆分后组合为单 store。 -
Zustand 与 Jotai 的适用场景有何不同? Zustand 是 store-based,适合管理聚合状态;Jotai 是 atom-based,适合管理细粒度的原子状态,随用随创建。
-
useSyncExternalStore和 Zustand 的关系? Zustand v4+ 底层使用useSyncExternalStore实现与 React 18 并发模式的兼容,确保状态更新在并发渲染中不会出现 tearing。 -
如何实现 Zustand Store 的按需加载? 将 store 定义放在动态 import 的模块中,使用时才加载;或用
lazy模式在首次访问时初始化状态。