Remix框架报错:Uncaught Error: Expected fetch controller 求助排查
排查React Router useFetcher.submit间歇性"Expected fetch controller"错误
问题场景
页面加载或路由切换时间歇性触发如下错误,同一操作多数情况下正常,仅偶尔报错。错误栈指向React Router内部的断言逻辑:
Uncaught Error: Expected fetch controller: :rb: at invariant3 (history.ts:494:11) at abortFetcher (router.ts:2599:5) at router.ts:2122:44 at Array.forEach (<anonymous>) at AbortSignal.abortPendingFetchRevalidations (router.ts:2122:28) at abortFetcher (router.ts:2600:16) at abortStaleFetchLoads (router.ts:2635:11) at handleFetcherAction (router.ts:2183:5) invariant3 @ history.ts:494 abortFetcher @ router.ts:2599 (anonymous) @ router.ts:2122 abortPendingFetchRevalidations @ router.ts:2122 abortFetcher @ router.ts:2600 abortStaleFetchLoads @ router.ts:2635 handleFetcherAction @ router.ts:2183 await in handleFetcherAction (async) fetch2 @ router.ts:1931 (anonymous) @ index.tsx:1551 (anonymous) @ index.tsx:1695 (anonymous) @ MyComponent.tsx:51 commitHookEffectListMount @ react-dom.development.js:23150 commitPassiveMountOnFiber @ react-dom.development.js:24926 commitPassiveMountEffects_complete @ react-dom.development.js:24891 commitPassiveMountEffects_begin @ react-dom.development.js:24878 commitPassiveMountEffects @ react-dom.development.js:24866 flushPassiveEffectsImpl @ react-dom.development.js:27039 flushPassiveEffects @ react-dom.development.js:26984 (anonymous) @ react-dom.development.js:26769 workLoop @ scheduler.development.js:266 flushWork @ scheduler.development.js:239 performWorkUntilDeadline @ scheduler.development.js:533
触发代码为组件mount时(useEffect空依赖)调用useFetcher的submit方法:
const fetcher = useFetcher(); useEffect(() => { fetcher.submit( null, { method: "post", action: "/some/route/" } ); }, [])
数据获取和组件渲染均正常,仅偶发上述错误。
原因分析
这个错误是React Router内部的断言失败,核心原因是请求生命周期与路由/组件切换时机冲突:
- useEffect空依赖在组件mount时立即发起请求,若此时路由正在切换(或组件未完全挂载完成),当路由切换触发中止pending请求的逻辑时,对应的AbortController实例已被内部清理,导致断言找不到控制器而报错。
- 即使请求最终成功完成,内部的中止逻辑仍会因状态不一致触发错误。
解决方案及排查步骤
- 给请求添加显式中止逻辑
利用useEffect的清理函数,手动管理请求的中止信号,避免内部状态不一致:
const fetcher = useFetcher(); useEffect(() => { const abortController = new AbortController(); fetcher.submit( null, { method: "post", action: "/some/route/", signal: abortController.signal // 传递中止信号 } ); // 组件卸载或effect重新执行时中止请求 return () => abortController.abort(); }, [fetcher]) // 添加fetcher到依赖,避免闭包陷阱
- 避免在路由切换临界状态发起请求
确保路由状态稳定后再执行submit,比如监听路由路径变化,或添加短延迟:
const { pathname } = useLocation(); const fetcher = useFetcher(); useEffect(() => { // 延迟100ms确保路由切换完成 const timer = setTimeout(() => { fetcher.submit( null, { method: "post", action: "/some/route/" } ); }, 100); return () => clearTimeout(timer); }, [fetcher, pathname])
升级React Router版本
该错误可能是特定v6.x版本的已知bug,建议升级到最新稳定版,修复内部并发请求与路由切换的逻辑漏洞。检查并发请求场景
若页面存在多个useFetcher请求,需确保它们的生命周期互不干扰,避免同时触发的请求在路由切换时互相影响。
内容的提问来源于stack exchange,提问作者SocaBlood
相关产品推荐
相关产品推荐

