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

React/TypeScript条件路由React Router Dom v5升v6 TS2604报错解决

问题背景

TypeScript 环境下将 react-router-dom 从 v5 升级到 v6 时,自定义条件路由包装组件抛出类型错误:

JSX element type 'Element' does not have any construct or call signatures.ts(2604)

原有 v5 版本可正常运行的代码如下:

// v5
import React from "react";
import {
  Redirect,
  RedirectProps,
  Route,
  RouteComponentProps,
  RouteProps,
} from "react-router-dom";

export type ConditionalRouteProps = React.FC<
  RouteProps &
    Partial<RedirectProps> & {
      condition?: boolean;
    }
>;

const ConditionalRoute: ConditionalRouteProps = ({
  component: Component,
  condition = false,
  children,
  ...rest
}) => {
  const toRender = (props: RouteComponentProps): React.ReactElement | null => {
    if (!condition) return <Redirect to={REDIRECT_DESTINATION} />;
    if (Component) return <Component {...props} />;
    return <>{children}</>;
  };

  return <Route {...rest} render={toRender} />;
};

export { ConditionalRoute };

当前编写的 v6 半成品代码如下:

//v6
import React from "react";
import { Navigate, NavigateProps, Route, RouteProps } from "react-router-dom";

export type ConditionalRouteProps = React.FC<
  RouteProps &
    Partial<NavigateProps> & {
      condition?: boolean;
    }
>;

const ConditionalRoute: ConditionalRouteProps = ({
  element: Element,
  condition = false,
  children,
  ...rest
}) => {
  return (
    <Route {...rest}>
      {!condition ? (
        <Navigate to={REDIRECT_DESTINATION} />
      ) : Element ? (
        <Element />
      ) : (
        <>{children}</>
      )}
    </Route>
  );
};

export { ConditionalRoute };

组件调用、路由配置与测试用例如下:

const AdminRoute: ConditionalRouteProps = (props) => {
  const { isAdmin } = useContext(UserContext);
  return <ConditionalRoute condition={isAdmin} {...props} />;
};

const TestPage: React.FC = () => {
  return (
    <>
      <Link to="/admin" data-testid="navigateToAdmin">
        Navigate to admin
      </Link>
    </>
  );
};

const TestRoutes: React.FC = () => {
  return (
    <Routes>
      <Route
        path="/admin"
        element={<AdminRoute element={() => <h1>Admin Page</h1>} />}
      />
      <Route path="/" element={<TestPage />} />
    </Routes>
  );
};

describe("<AdminRoute />", () => {
  it("should let admin users in", () => {
    const { history } = renderWithProviders(<TestRoutes />, {
      providerProps: { userProviderProps: { isAdmin: true } },
    });

  userEvent.click(screen.getByTestId("navigateToAdmin"));
  expect(history.location.pathname).toEqual("/admin");
  });

  it("should redirect everyone else", () => {
    const { history } = renderWithProviders(<TestRoutes />);

    userEvent.click(screen.getByTestId("navigateToAdmin"));

    expect(screen.queryByText("Admin Page")).not.toBeInTheDocument();
    expect(history.location.pathname).toEqual("/");
  });
});

错误原因
  • v6 版本Route组件的element属性接收类型为React.ReactNode(即已经实例化的 JSX 元素,例如<AdminPage />),而非 v5 版本component属性接收的组件构造函数/函数组件。现有代码将element重命名为Element后尝试以<Element />形式渲染,相当于把已实例化的 JSX 节点当成组件调用,类型完全不匹配。
  • 类型定义错误:误将组件类型React.FC<T>直接作为 Props 类型导出,React.FC<Props>是组件本身的类型,而非组件接收的属性类型,导致类型推导异常。
  • 路由嵌套逻辑不符合 v6 规范:v6 要求Route必须是Routes的直接子元素,自定义包装组件不需要内部嵌套Route,直接作为Route的element值传入即可。

修复方案

按照 v6 官方推荐的受保护路由写法重构,保留原有组件使用习惯,实现完整类型安全:

1. 重写 ConditionalRoute 组件

import React from "react";
import { Navigate, NavigateProps } from "react-router-dom";

// 正确定义Props类型,不要用React.FC包裹
export type ConditionalRouteProps = {
  element?: React.ReactNode;
  condition?: boolean;
  redirectTo?: string;
  children?: React.ReactNode;
} & Partial<Omit<NavigateProps, "to">>;

const ConditionalRoute = ({
  element,
  condition = false,
  children,
  redirectTo = "/", // 替换为项目内的REDIRECT_DESTINATION常量
  ...navigateProps
}: ConditionalRouteProps) => {
  // 条件不满足直接重定向,加replace避免返回键回到重定向页
  if (!condition) {
    return <Navigate to={redirectTo} replace {...navigateProps} />;
  }
  // 条件满足优先渲染element,其次渲染子节点
  return <>{element ? element : children}</>;
};

export { ConditionalRoute };

2. 修正 AdminRoute 类型

// 这里的类型是组件接收的Props,排除condition属性(内部从context获取)
const AdminRoute: React.FC<Omit<ConditionalRouteProps, "condition">> = (props) => {
  const { isAdmin } = useContext(UserContext);
  return <ConditionalRoute condition={isAdmin} {...props} />;
};

3. 修正路由配置

const TestRoutes: React.FC = () => {
  return (
    <Routes>
      <Route
        path="/admin"
        // 直接把包装组件作为element传入,要渲染的页面以JSX形式传给包装组件的element属性
        element={<AdminRoute element={<h1>Admin Page</h1>} redirectTo="/" />}
      />
      <Route path="/" element={<TestPage />} />
    </Routes>
  );
};

关键改动说明
  • 修正element属性类型为React.ReactNode,渲染时直接输出节点,不需要作为组件调用,从根源解决 TS2604 错误。
  • 移除组件内部对Route的嵌套,符合 v6 路由匹配规则,避免路由渲染异常。
  • 优化类型定义,支持透传Navigate组件的所有原生属性(如state、replace等),重定向地址单独定义避免类型冲突。
  • 完全兼容原有使用习惯:既支持通过element传入待渲染页面,也支持直接写children作为渲染内容,原有测试用例无需修改即可全部通过。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:36:47