简体   繁体   中英

React router DOM : Switch not working if child element isnt a route

I'm facing an issue im not able to comprehend. I'm working on a React codebase and we started having too much Routes, so i decided to refactor it a little bit. There is some logic to determine if we should redirect to a route that will check if you're authenticated, and if not will redirect you to the sign in page. We also have basic routes without that mechanism that just display a component ( for every page that doesnt need authentication like sign in, forgot password etc. )

I've decided to abstract that logic in a different component, that will ultimatley render a Route.

Here's the code for the routes:

      <Switch>

        {Object.values(userRoutes).map((route: SanitizedRoute) => (
          <RouteController route={route} key={route.PATH} />
        ))}
      </Switch>

RouterController.tsx

  return (
    <Route
      path={route.PATH}
      exact={route.EXACT}
      render={() => {
        return <Layout>{route.COMPONENT}</Layout>;
      }}
    />
  );

All the information for the Route component is passed down as a prop. If i try to access /path_b what is rendered is the first route of my userRoute array. Accessing any path actually returns the first route as if it was the only one outputted by the map loop.

If i were to replace the RouteController component by its content as such:

<Switch>
    {Object.values(userRoutes).map((route: SanitizedRoute) => (
          <Route
            path={route.PATH}
            exact={route.EXACT}
            key={route.PATH}
            render={() => {
              return <Layout>{route.COMPONENT}</Layout>;
            }}
          />
        ))}
</Switch>

Then everything would work fine as expected. Wrapping my RouteController with a Switch component is also working fine - although im not sure about the side effect of having a Switch per route?

      {Object.values(userRoutes).map((route: SanitizedRoute) => (
        <Switch key={route.PATH}>
          <RouteController route={route} />
        </Switch>
      ))}

My questions are:

  • Why isn't it possible to wrap all of those routes in a single?
  • Is there some props from the component i should manually pass down to the component via my?
  • I know that the is used to render the first route that match the path. Does having multiple kind of defeat the purpose of using a in the first place?

Why isn't it possible to wrap all of those routes in a single <Switch> ?

This is because of how the Switch component works, it renders the first child <Route> or <Redirect> that matches the location.

Switch

The RouteController component is neither, so it's not involved in matching, and in fact only first component will be rendered. This is why you see only the first route rendered regardless of path.

Is there some props from the <Switch> component I should manually pass down to the <Route> component via my <RouterController> ?

It's possible to move/define a path prop ( and any other "route" prop ) on the RouteController the Switch may use for path matching, but this is ill-advised and not a documented use case of the Switch and Route components.

I know that the <Switch> is used to render the first route that match the path. Does having multiple <Switch> kind of defeat the purpose of using a <Switch> in the first place?

Yes, if you are rendering multiple Switch components, each with only a single Route component then they aren't really switching routes at this point and you're relying on the inclusive route matching of any wrapping router, or exclusively matching in the case of any other wrapping switches.

Solution

Instead of trying to map routes into your RouterController component and dealing with the issues of what is directly composing what, have your RouterController consume all the routes as a prop, and render them into a single Switch component.

RouterController.tsx

const RouterController = ({ routes }) => {
  // any component business logic

  return (
    <Switch>
      {routes.map((route: SanitizedRoute) => (
        <Route
          path={route.PATH}
          exact={route.EXACT}
          key={route.PATH}
          render={() => <Layout>{route.COMPONENT}</Layout>}
        />
      ))}
    </Switch>
  );
};

...

<RouterController routes={Object.values(userRoutes)} />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM