在闭包中调用Zustand create时状态更新不生效的问题
问题原因与修复方案
核心原因
- 重复创建Store实例:每次组件渲染时,
usePaginationData(mockPageDataRequest)()都会重新执行create方法,生成全新的Zustand Store实例。组件每次拿到的都是新Store的初始状态,自然看不到之前Store中isLoading的变化。 - 状态监听方式错误:直接在组件渲染阶段打印
isLoading,这里打印的是当前渲染周期的状态快照,不会自动追踪后续状态更新。
修复代码
第一步:改造自定义Store Hook,实现单例缓存
// 用WeakMap缓存不同requestMethod对应的Store实例,避免重复创建 const storeCache = new WeakMap(); export const usePaginationData = (requestMethod) => { // 不存在对应Store时才创建 if (!storeCache.has(requestMethod)) { const store = create((set, get) => ({ data: [], page: 1, isLoading: false, fetchMore: async () => { set({ isLoading: true }); try { const newData = await requestMethod(get().page, 10); set((state) => ({ data: [...state.data, ...newData.data], page: state.page + 1 // 必须更新页码,否则每次请求同一页数据 })); } finally { set({ isLoading: false }); // 请求完成(成功/失败)都重置加载状态 } }, })); storeCache.set(requestMethod, store); } // 返回缓存后的Store的useStore钩子 return storeCache.get(requestMethod); };
第二步:组件中正确调用并监听状态
import { useEffect } from 'react'; // 直接调用返回的useStore钩子,符合React Hook规则 const { data, isLoading, fetchMore } = usePaginationData(mockPageDataRequest)(); // 用useEffect监听isLoading的变化,才能打印实时状态 useEffect(() => { console.log('isLoading', isLoading); }, [isLoading]); return ( <button onClick={fetchMore} disabled={isLoading}> {isLoading ? '加载中...' : '加载更多'} </button> );
关键说明
- WeakMap缓存:利用
WeakMap的特性,当requestMethod函数被垃圾回收时,对应的Store也会自动清理,不会造成内存泄漏。 - 单例Store:同一
requestMethod只会创建一次Store,组件每次拿到的都是同一个Store的状态,状态更新会触发组件重新渲染。 - 状态监听:组件渲染阶段的
console.log只能打印当前渲染的状态值,要追踪状态变化必须用useEffect监听依赖项。 - 完善逻辑:补充了页码更新和
finally块重置isLoading,避免请求失败时一直显示加载状态,同时防止重复请求同一页数据。
内容的提问来源于stack exchange,提问作者Kent Wood
相关产品推荐
相关产品推荐

