使用MobX的React应用加载状态失效,加载指示器不显示
React + MobX 加载指示器不显示问题排查
问题场景
当前开发的连接 WordPress REST API 的 React 应用,全链路数据获取功能运行正常,但加载指示器始终无法展示,数据加载过程中页面为空白。
技术实现细节:
- 使用 MobX 做状态管理,定义了可观察属性
loadingInitial,预期逻辑为触发 action 时将该属性置为 true 进入加载态,action 完成所有操作后重置为 false,实现数据请求时展示加载页面的效果 - 展示文章列表的组件已经通过
observer方法包装为可观测组件
现有实现代码
MobX 动作逻辑
@action loadAnecdotes = async (page: number, year: number, order: string) => { this.loadingInitial = true try { const anecdotesHeaders = year === 0 ? await agent.AnecdotesHeaders.list() : await agent.AnecdotesHeaders.listByYear(year) const maxPages = anecdotesHeaders['x-wp-totalpages'] if (page <= maxPages) { const anecdotes = year === 0 ? await agent.Anecdotes.list(page, order) : await agent.Anecdotes.listByYear(page, year, order) runInAction(() => { this.anecdoteArray = [] anecdotes.forEach((anecdote, i) => { this.anecdoteArray[i] = anecdote }) this.loadingInitial = false }) } else { runInAction(() => { this.loadingInitial = false }) } return {anecdoteArray: this.anecdoteArray, maxPages} } catch (error) { console.log(error) this.loadingInitial = false } }
组件内数据获取逻辑
useEffect(() => { loadAnecdotes(page, year, order).then(result => { if (page <= result?.maxPages) setArray(a => [...a, ...result?.anecdoteArray!]) setLoaded(true) }) }, [loadAnecdotes, page, year, order, setLoaded, setArray])
加载态判断逻辑
if (loadingInitial) return <LoadingComponent content='Anecdotes loading...' />
核心问题定位
- 异步上下文的状态更新脱离 action 边界。MobX 的
@action装饰器只会包裹函数的同步执行段,当代码遇到第一个await进入异步上下文后,后续的状态变更不会被自动纳入 action 边界。当前代码中 catch 块里的this.loadingInitial = false完全处于异步上下文,没有用runInAction包裹,在严格模式下这类状态变更不会被 MobX 追踪,也不会触发组件重新渲染。 - 状态更新通知被 React 批处理合并。React 18 的自动批处理机制会把同一执行周期内的所有状态变更合并成一次渲染,当前流程中
loadingInitial从 false → true → false 的整个状态变化,都可能被合并到同一次渲染周期里,组件根本没有机会单独渲染加载态,直接展示最终的文章列表状态。 - 可观察值追踪失效风险。如果在组件中通过直接解构 store 的方式获取
loadingInitial,解构操作会丢失 MobX 的可观察绑定,拿到的只是初始时刻的静态值,哪怕组件被observer包裹,也无法感知后续的状态变化。
修复方案
- 把所有异步上下文的状态更新统一用
runInAction包裹,包括 catch 块中的状态重置逻辑,确保所有状态变更都在 MobX 的 action 边界内,能被正确追踪和通知。 - 调整组件内可观察值的读取方式,不要在组件顶层直接解构 store 的属性,改为在渲染逻辑中通过 store 实例直接访问属性(例如
if (store.loadingInitial) ...),保证 observer 能正确收集依赖,触发重新渲染。 - 避开 React 批处理的合并影响:如果调整后依然无法展示加载态,可以将数据请求逻辑放到
setTimeout(0)的宏任务中,让设置加载状态的同步逻辑先完成渲染,再发起数据请求,确保加载态有独立的渲染周期。
内容的提问来源于stack exchange,提问作者aademirci
相关产品推荐
相关产品推荐

