
Zustand 的 useStore Hook 不能在顶层调用,但可通过 getState() 和 setState() 方法在工具函数、副作用、API 调用等非组件上下文中安全读写状态。
zustand 的 `usestore` hook 不能在顶层调用,但可通过 `getstate()` 和 `setstate()` 方法在工具函数、副作用、api 调用等非组件上下文中安全读写状态。
Zustand 的核心设计哲学之一是“状态即数据”,其 store 实例本身是可直接操作的独立对象——它不仅支持 Hook 形式(useStore()),更提供了面向普通 JavaScript 环境的底层 API:getState()、setState() 和 subscribe()。这意味着你完全可以在 .ts 工具文件、事件回调、定时器、Axios 拦截器甚至 Web Worker 中与 store 交互,而无需依赖 React 渲染上下文。
✅ 正确做法:使用 getState() 和 setState()
将你的 util.ts 改写为如下方式(关键改动已高亮):
// @/store/useStore.ts import { create } from 'zustand'; interface Room { room_no: number; adult: number; child: number; infant: number; } interface StoreState { roomState: Room[]; setRoomState: (rooms: Room[]) => void; } const useStore = create<StoreState>((set) => ({ roomState: [], setRoomState: (rooms) => set({ roomState: rooms }), })); export default useStore;
// @/utils/util.ts import useStore from '@/store/useStore'; // ✅ 安全:直接调用 store 方法,不使用 Hook const utilityFunctions = { sha512: async (str: string): Promise<string> => { const buf = await crypto.subtle.digest('SHA-512', new TextEncoder().encode(str)); return Array.from(new Uint8Array(buf)) .map((b) => b.toString(16).padStart(2, '0')) .join(''); }, addRoom: (min_inv: number) => { const currentState = useStore.getState(); // ? 读取当前 state if (currentState.roomState.length < min_inv) { useStore.setState({ roomState: [ ...currentState.roomState, { room_no: currentState.roomState.length + 1, adult: 0, child: 0, infant: 0, }, ], }); } }, // ✅ 更推荐:将 state 更新逻辑封装进 store 内部(提升可维护性) addRoomSafely: (min_inv: number) => { useStore.setState((state) => { if (state.roomState.length >= min_inv) return state; return { roomState: [ ...state.roomState, { room_no: state.roomState.length + 1, adult: 0, child: 0, infant: 0, }, ], }; }); }, }; export default utilityFunctions;
⚠️ 注意事项与最佳实践
- 不要解构 useStore() 返回值到顶层:如 const { roomState, setRoomState } = useStore() 是非法的,会触发 ESLint react-hooks/rules-of-hooks 报错。
- 优先使用 setState(callback) 形式:它接收前一个 state,保证更新基于最新快照(尤其在异步或高频调用场景下避免竞态)。
- 避免在工具函数中长期持有 getState() 返回的引用:Zustand 的 getState() 返回的是当前快照(浅拷贝),不是响应式引用;如需监听变化,请用 subscribe()(适用于非组件逻辑,如日志、分析上报)。
- 类型安全提示:确保 create
的泛型声明完整,TypeScript 将自动推导 getState() 和 setState() 的类型,大幅提升开发体验。
✅ 总结
Zustand 天然支持「脱离 React 组件」的状态管理。只需放弃在工具层调用 Hook 的惯性思维,转而使用 useStore.getState() 读取、useStore.setState() 写入,即可优雅解决跨上下文状态操作问题。这不仅消除了 ESLint 报错,更让状态逻辑真正实现关注点分离——UI 层专注渲染,工具层专注业务,store 本身成为统一、可测试、可复用的状态中枢。