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

React中BrowserRouter上下文在孙组件丢失,useNavigate报错排查

问题:React Router上下文与Hook调用冲突问题

我在React项目中原本用BrowserRouter包裹ErrorBoundary和App组件,App组件在开发环境下使用useNavigate钩子能正常运行,但ESLint提示React Hook不能被条件式调用。为解决这个问题,我把开发环境逻辑拆分到MySecurityComponent及其子组件MySecurityComponentDev中,却出现错误:useNavigate() may be used only in the context of a <Router> component。想请教是不是遗漏了上下文传递步骤,或是Router上下文只能在顶层子组件和路由内使用?


相关代码

初始化代码

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter>
      <ErrorBoundary FallbackComponent={CustomError}>
        <App />
      </ErrorBoundary>
    </BrowserRouter>
  </React.StrictMode>,
)

初始App组件(ESLint报错版本)

export const App = () => {
  if (isDevEnv()) {
    const navigate = useNavigate();
    
    const restoreOriginalUri = async (originalUri: string) => {
      navigate(toRelativeUrl(originalUri || '/', window.location.origin));
    };

    return (
      <Security restoreOriginalUri={restoreOriginalUri}>
        <AppRoutes />
      </Security>
    )
  }

  return (
    <AppRoutes />
  )
}

拆分后的App组件

export const App = () => {
  const children: React.JSX.Element = <AppRoutes />;
  return <MySecurityComponent children={children} />
}

MySecurityComponent组件

type MySecurityComponentType = (props: { children: React.JSX.Element }) => React.JSX.Element;

export const MySecurityComponent: MySecurityComponentType = ({children}): React.JSX.Element => {
  if (isDevEnv()) {
    return <MySecurityComponentDev children={children} />
  }
  return children;
}

MySecurityComponentDev组件

type MySecurityComponentDevType = (props: { children: React.JSX.Element }) => React.JSX.Element;

const MySecurityComponentDev: MySecurityComponentDevType = ({ children }) => {
  const navigate = useNavigate();

  const restoreOriginalUri = async (originalUri: string) => {
    navigate(toRelativeUrl(originalUri || '/', window.location.origin));
  };

  return (
    <Security restoreOriginalUri={restoreOriginalUri}>
      {children}
    </Security>
  );
}

解决方案

问题核心

你遇到的错误并非Router上下文传递遗漏,而是提前渲染了AppRoutes并作为已完成的JSX元素传递,导致组件上下文绑定时机异常。同时Router上下文可传递给所有内部嵌套组件,不存在层级限制。

修正方案一:内联组件处理开发逻辑

将开发环境逻辑封装为内部组件,确保useNavigate在组件顶层调用,既符合Hook规则,又保证组件处于Router上下文内:

export const App = () => {
  // 开发环境专用组件,Hook在顶层调用
  const DevSecurityWrapper = () => {
    const navigate = useNavigate();
    
    const restoreOriginalUri = async (originalUri: string) => {
      navigate(toRelativeUrl(originalUri || '/', window.location.origin));
    };

    return (
      <Security restoreOriginalUri={restoreOriginalUri}>
        <AppRoutes />
      </Security>
    );
  };

  return isDevEnv() ? <DevSecurityWrapper /> : <AppRoutes />;
};

修正方案二:调整组件传递方式(适配你的拆分思路)

修改组件接收的children为组件类型而非已渲染元素,确保AppRoutes在正确的上下文内渲染:

  1. 修改MySecurityComponent:
// 类型定义改为接收组件而非已渲染元素
type MySecurityComponentType = (props: { children: React.ComponentType }) => React.JSX.Element;

export const MySecurityComponent: MySecurityComponentType = ({ children: ChildrenComponent }) => {
  if (isDevEnv()) {
    return <MySecurityComponentDev>
      <ChildrenComponent />
    </MySecurityComponentDev>;
  }
  return <ChildrenComponent />;
};
  1. 修改App组件:
export const App = () => {
  // 传递组件本身,而非已渲染的JSX元素
  return <MySecurityComponent children={AppRoutes} />;
};
  1. MySecurityComponentDev保持不变(仅调整类型适配ReactNode):
type MySecurityComponentDevType = (props: { children: React.ReactNode }) => React.JSX.Element;

const MySecurityComponentDev: MySecurityComponentDevType = ({ children }) => {
  const navigate = useNavigate();

  const restoreOriginalUri = async (originalUri: string) => {
    navigate(toRelativeUrl(originalUri || '/', window.location.origin));
  };

  return (
    <Security restoreOriginalUri={restoreOriginalUri}>
      {children}
    </Security>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 15:27:02