react-router-dom 有条件地构建路由并将参数传递给基于 class 的组件

react-router-dom conditionally build routes and pass parameters to class based component

我正在使用 react-router-dom 构建一个应用程序,它通过发出 AJAX 请求进行初始化,然后使用路由将参数传递给基于 class 的子组件。我不知道如何同时 1) 有条件地渲染 <Routes> 和 2) 将参数传递给子组件。

我有一个功能正常的父级 <RouterApp /> 组件,以及一个有条件呈现的组件 <MyClassBasedComponent>

在我最初的尝试中,我这样设置我的代码:

function RouterApp() {
  const preloadData = usePreloadData(); /*custom hook to make an AJAX call*/
  
  /*Conditionally show loader screen or app content*/
  if (!preloadData) {
    return <WelcomeLoader />;
  } else {
    //build routes obj
    const routesObj = {
      "/": () => (props) => <About />,
      "/component/:clientid": (clientID) => <MyClassBasedComponent 
                                             essentialData={preloadData}
                                             clientID={clientID}/>,
    };


    return (
      <Router>
        <HeaderMenu />
          <div className="app-body router-app">
             {useRoutes(routesObj)}
          </div>
      </Router>
    );
    
  }
}

这会引发编译错误: React Hook "useRoutes" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

我的下一次尝试是以字典数组形式创建一个路由对象:

function RouterApp() {
  const preloadData = usePreloadData(); /*custom hook to make an AJAX call*/
  
  /*Conditionally show loader screen or app content*/
  if (!preloadData) {
    return <WelcomeLoader />;
  } else {
    //build routes obj
    
    const routesObj = [
      { path: "/", element: <About /> },
      { path: "/component/:clientid", "element": (clientid) => (<MyClassBasedComponent
                                             essentialData={preloadData}
                                             clientID={clientid}/>) },
      //Various other attempts below
      //{ path: "/component", element: <ClassComponentWithLink/> },
      //{ path: "/component/:clientid", element: (clientid)=><ClassComponentWithLink id={clientid}/> },
      //{ path: "/component/:clientid", element: <ClassComponentWithLink myProp={clientid}/> },
      
    ];
    const MyRoutes = (props) => useRoutes(props.routesObj);


    return (
      <Router>
        <HeaderMenu />
          <div className="app-body router-app">
             <MyRoutes 
                  routesObj = {routesObj}
              />
          </div>
      </Router>
    );
    
  }
}

当我使用此表单并将函数传递给“元素”时,<MyClassBasedComponent /> 不呈现。

当我使用表单 { path: "/component/:clientid", element: <MyClassBasedComponent /> }, 时,组件呈现,但是当我询问它的 props 时,我在任何地方都看不到 clientid 的值。

我怎样才能正确地完成这个?

Here is the relevant documentation.

两种实现方式都不正确。在 react-router-dom@6 中不再有任何路由道具(historylocationmatch),并被 React hooks 取代。由于您还使用了 React class 组件,因此无法直接使用挂钩,因此您需要将 class 组件转换为函数组件,或者创建包装器组件或自定义withRouter HOC 访问钩子并传递 along/inject 旧路由道具。我不会介绍将 class 组件转换为函数组件。

  • 包装示例

    创建一个使用 useParams 挂钩读取 clientid 路由参数并呈现 MyClassBasedComponent 组件的包装器组件。

      const MyClassBasedComponentWrapper = () => {
        const { clientId } = useParams();
        return (
          <MyClassBasedComponent
            essentialData={preloadData}
            clientID={clientid}
          />
        );
      };
    

    然后按照预期指定路由配置。

    const routesObj = [
      { path: "/", element: <About /> },
      {
        path: "/component/:clientid",
        element: <MyClassBasedComponentWrapper />
      },  
    ];
    
  • 自定义withRouter示例

    const withRouter = Component => props => {
      const params = useParams();
      ... use the other hooks here if you need them ...
      return <Component {...props} params={params} />;
    };
    

    修饰 MyClassBasedComponent 组件,使其接收 params 作为道具。

     export default withRouter(MyClassBasedComponent);
    

    然后按照预期指定路由配置。

    const routesObj = [
      { path: "/", element: <About /> },
      {
        path: "/component/:clientid",
        element: <MyClassBasedComponent />
      },  
    ];
    

    MyClassBasedComponent 组件中访问 this.props.params 中的 clientId 参数。

     `const { clientId } = this.props.params;
    

正在配置 RouterApp 组件:

const routesObj = [
  { path: "/", element: <About /> },
  {
    path: "/component/:clientid",
    element: <MyClassBasedComponent />
  },  
];

function RouterApp() {
  const preloadData = usePreloadData();
  const routes = useRoutes(routesObj);
  
  return !preloadData
    ? <WelcomeLoader />
    : (
      <Router>
        <HeaderMenu />
        <div className="app-body router-app">
          {routes}
        </div>
      </Router>
    );
  };
}