
Zustand 的自定义 Hook(如 useStore)不能在组件外部直接调用,但可通过 getState() 和 setState() 方法在工具函数、副作用、API 调用等非 React 渲染上下文中安全读写状态。
zustand 的自定义 hook(如 `usestore`)不能在组件外部直接调用,但可通过 `getstate()` 和 `setstate()` 方法在工具函数、副作用、api 调用等非 react 渲染上下文中安全读写状态。
Zustand 的核心设计原则之一是:状态逻辑与 UI 解耦。虽然 useStore 是一个 React Hook,必须在组件或自定义 Hook 内部调用,但 Zustand 实例本身提供了不依赖 React 生命周期的底层 API —— getState()、setState() 和 subscribe()。这使得你在 utils.ts、api.ts、effects.ts 甚至 setTimeout 回调中也能可靠地操作全局状态。
✅ 正确做法:使用 Zustand 实例方法替代 Hook 调用
假设你的 store 定义如下(推荐使用 create + 显式 action):
// @/store/useStore.ts
import { create } from 'zustand';
type Room = {
room_no: number;
adult: number;
child: number;
infant: number;
};
type StoreState = {
roomState: Room[];
setRoomState: (rooms: Room[]) => void;
addRoom: () => void;
};
const useStore = create<StoreState>((set, get) => ({
roomState: [],
setRoomState: (rooms) => set({ roomState: rooms }),
addRoom: () => {
const { roomState } = get();
const nextRoom: Room = {
room_no: roomState.length + 1,
adult: 0,
child: 0,
infant: 0,
};
set({ roomState: [...roomState, nextRoom] });
},
}));
export default useStore;? 关键点:useStore 是一个 Zustand store 工厂函数返回的实例,它本身不是 Hook,而是具备 getState()、setState() 等静态方法的对象。
✅ 在工具文件中安全访问状态(无 Hook 调用)
修改你的 util.ts,移除顶层 useStore() 调用,改用 useStore.getState() 和 useStore.setState():
// utils.ts
import useStore from '@/store/useStore';
import { crypto } from '@std/crypto'; // 或使用全局 crypto(注意浏览器环境兼容性)
const utilityFunctions = {
sha512: async (str: string): Promise<string> => {
const encoder = new TextEncoder();
const hashBuffer = await crypto.subtle.digest('SHA-512', encoder.encode(str));
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
},
addRoom: (min_inv: number) => {
const { roomState } = useStore.getState();
if (roomState.length < min_inv) {
useStore.setState({
roomState: [
...roomState,
{
room_no: roomState.length + 1,
adult: 0,
child: 0,
infant: 0,
},
],
});
}
},
// 若需复用逻辑,也可封装为可组合函数
ensureMinRooms: (min: number) => {
const { roomState } = useStore.getState();
const toAdd = Math.max(0, min - roomState.length);
const newRooms = Array.from({ length: toAdd }, (_, i) => ({
room_no: roomState.length + i + 1,
adult: 0,
child: 0,
infant: 0,
}));
useStore.setState({
roomState: [...roomState, ...newRooms],
});
},
};
export default utilityFunctions;⚠️ 注意事项与最佳实践
- 不要在顶层执行 useStore():useStore() 是 Hook,仅限组件或自定义 Hook 内调用;而 useStore.getState() 是普通函数,可任意位置使用。
-
避免频繁 getState() + setState() 拆分操作:如需基于当前状态计算新值(如 push、filter),优先使用 useStore.setState((state) => newState) 形式,确保原子性和并发安全:
useStore.setState((state) => ({ roomState: [...state.roomState, newRoom], })); - 异步操作中注意状态时效性:getState() 返回的是调用瞬间的快照。若涉及多个异步步骤,建议在关键节点重新 getState(),或使用 subscribe() 监听变更(适用于长期监听场景)。
- TypeScript 类型安全:useStore.getState() 返回完整状态类型,编辑器可自动推导属性,无需额外断言。
- 服务端渲染(SSR)兼容性:getState() 在服务端可用,但需确保 store 初始化逻辑不依赖浏览器 API(如 crypto.subtle 需 polyfill 或条件判断)。
✅ 总结
Zustand 并非“只能配合 React 使用”——它的状态管理能力天然支持跨上下文操作。通过 getState() 读取、setState() 更新、subscribe() 响应,你可以在工具函数、请求拦截器、定时任务、Web Worker(配合序列化)等任意 JS 环境中与 Zustand 交互。这不仅解决了 ESLint 报错问题,更体现了 Zustand “轻量、灵活、可组合”的设计哲学。










