You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例已被内部清理,导致断言找不到控制器而报错。
  • 即使请求最终成功完成,内部的中止逻辑仍会因状态不一致触发错误。

解决方案及排查步骤

  1. 给请求添加显式中止逻辑
    利用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到依赖,避免闭包陷阱
  1. 避免在路由切换临界状态发起请求
    确保路由状态稳定后再执行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])
  1. 升级React Router版本
    该错误可能是特定v6.x版本的已知bug,建议升级到最新稳定版,修复内部并发请求与路由切换的逻辑漏洞。

  2. 检查并发请求场景
    若页面存在多个useFetcher请求,需确保它们的生命周期互不干扰,避免同时触发的请求在路由切换时互相影响。

内容的提问来源于stack exchange,提问作者SocaBlood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:10:33