React Context循环依赖:useB读取useA返回值出现stale数据如何解决
问题根因
你的代码问题本质是同组件内的Context读写循环:AppContextProvider组件内部先调用useA、useB生成Context值,再把值传入Provider。而useB内部消费的是当前已挂载的Provider的旧值,第一次渲染时拿到的永远是Context的初始值null,自然取不到正确的a返回结果。
最优解决方案:调整useB的参数传递逻辑
直接将useA的返回值作为参数传入useB,跳过useB内部消费Context的步骤,从根源避免旧值问题,同时不会触发useA重复调用。
修改后的代码如下:
const useA = () => { const api = useApi() const [state, setState] = useState(null) useEffect(() => { api.someFn().then((result) => { setState(result) }) }, [api]) return state } // 调整useB,接收a作为入参,不需要内部消费AppContext const useB = (a) => { const api = useApi() const [state, setState] = useState(null) useEffect(() => { // 增加非空判断,a加载完成前不发起无效请求 if (!a) return api.someOtherFn(a.someVariable).then((result) => { setState(result) }) }, [api, a]) return state } const AppContext = createContext({ a: null, b: null }) const AppContextProvider = ({children}) => { const a = useA() // 直接把获取到的a传入useB const b = useB(a) const context = { a, b } return <AppContext.Provider value={context}>{children}</AppContext.Provider> } const App = () => { return ( <AppContextProvider> <Router> ... </Router> </AppContextProvider> ) }
该方案的优势:
- 依赖逻辑清晰,
useB完全跟随a的更新触发请求,不会拿到旧值 useA仅在Provider内调用一次,不会产生重复的开销请求- 全局Context的结构保持不变,其他组件仍可正常消费
a和b的值
备选方案(不能修改useB入参时使用)
如果因为业务限制不能修改useB的定义,可以拆分Context层级,将a的存储和b的存储拆分为两层Provider:
- 单独创建
AContext存储useA的返回值,先挂载AProvider - 内层的
AppContextProvider和useB都消费AContext获取a的值 - 最终将
a和b一起存入AppContext对外暴露
优化建议
- 给两个网络请求增加取消逻辑,避免组件卸载后请求完成触发无意义的
setState报错 - 给
useA增加加载态、错误态的返回,方便后续业务层处理加载、异常场景
内容的提问来源于stack exchange,提问作者MikkoP
相关产品推荐
相关产品推荐

