Next.js状态管理中异步调用store方法后数据未更新问题排查
问题背景
在Next.js中使用Zustand做状态管理时,调用异步的store方法getSites并通过await等待后,在useEffect中获取的sites数据仍为空。控制台先输出in store的有效数据,再输出in useEffect的空数组,给set方法加await也无效。后来改用直接赋值this.sites = data解决了问题,但希望使用官方提供的set方法,并明确原写法出错的原因。
原代码问题原因
1. Zustand的set方法不返回Promise
你给set({ sites: data })加await没有效果,是因为Zustand的set方法不返回Promise——它是异步更新状态,但不会被await等待。因此await trackingStore.getSites()只能等待axios请求完成,无法等待store状态更新完成。
2. 直接访问store属性不是响应式快照
组件中trackingStore.sites是组件挂载时拿到的初始状态引用,即使store内部已经更新了状态,这个旧引用也不会自动更新。因为你没有用Zustand的useStore Hook订阅状态变化,useEffect的回调只会执行一次,拿到的始终是挂载时的空数组。
另外你用this.sites = data能生效,是因为这种写法直接修改了store实例的属性,但这属于不规范用法,官方推荐用set方法更新状态,避免状态更新不被追踪。
正确解决方案(使用官方set方法)
方案1:用useStore订阅状态,让组件响应更新
在组件中通过useStore订阅sites状态,状态更新时组件会自动重新渲染并拿到最新值:
import { useStore } from 'zustand'; import trackingStore from './trackingStore'; // 组件内 const sites = useStore(trackingStore, state => state.sites); const isSitesFetching = useStore(trackingStore, state => state.isSitesFetching); useEffect(() => { trackingStore.getSites(); }, []); // 依赖sites状态,当状态更新时触发逻辑 useEffect(() => { if (sites?.length > 0) { // 处理sites数据 } }, [sites]);
方案2:让getSites返回数据,直接在useEffect中使用
修改store的getSites方法,返回请求到的数据,这样await后就能直接拿到最新值:
// trackingStore.js getSites: async () => { set({ sites: [], isSitesFetching: true }); const token = useAuthStore.getState().token; try { const { data } = await axios.get<Site[]>(`${apiUrl}/settings/get-sites`, { timeout: 90000000, headers: { Authorization: `Bearer ${token}` }, }); set({ sites: data }); console.log('in store', data); return data; // 返回请求到的数据 } catch (e) { console.log(e); return []; } finally { set({ isSitesFetching: false }); } },
组件中调用:
useEffect(() => { const fetchData = async () => { const sites = await trackingStore.getSites(); console.log('in useEffect', sites); if (sites?.length > 0) { // 使用sites数据 } }; fetchData(); }, []);
这样既能遵循官方规范使用set方法更新状态,又能在useEffect中拿到最新数据,同时store状态也会同步到其他订阅的组件。
内容的提问来源于stack exchange,提问作者zzmaster

