2

I'm using react-router to set up my application, and try to use <Redirect/> to set router for authentication.

Routecomponent have two different components, one is private Route, another is public route.

Expect result : when auth is false, the page should jump back to a public page, which I set <Redirect to={"/public"} /> so far it seems route works fine, but redirect doesn't work properly. Any ideas are welcome! Thanks!!

PrivateRoute

interface PrivateRouteProps {
  isLogin: boolean;
  privateRoutes: RouteItem[];
}

const PrivateRoute: React.FunctionComponent<PrivateRouteProps> = (
  props: PrivateRouteProps
) => {
  return (
    <>
      {props.isLogin ? (
        props.privateRoutes.map(item => {
          return <Route key={item.path} {...item} />;
        })
      ) : (
        <Redirect to={PUBLIC.path} />
      )}
    </>
  );
};

PublicRoute

interface PublicProps {
  publicRoutes: RouteItem[];
}

const PublicRoute: React.FC<PublicProps> = (props: PublicProps) => {
  return (
    <>
      {props.publicRoutes.map(route => (
        <Route key={route.path} {...route} />
      ))}
    </>
  );
};

Route

<BrowserRouter>
      <Switch>
        <PublicRoute publicRoutes={publicRoutes} />
        <PrivateRoute privateRoutes={privateRoutes} isLogin={login} />
      </Switch>
    </BrowserRouter>

UPDATE As the accepted answer mentioned, it's all about <Switch/> works with Fragment, so I modified my routes as following, it works like a charm. Just update it for someone may have similar question.

<BrowserRouter>

            <Switch>
                {publicRoutes.map(item => {
                    return <Route key={item.path} {...item}/>
                })}
                {privateRoutes.map(item => {
                    return <PrivateRoute key={item.path}
                                         exact={item.exact}
                                         component={item.component}
                                         path={item.path}
                                         redirectPath={SIGN_IN.path}
                    />

      })}
            </Switch>
        </BrowserRouter>

1 Answers1

3

I have gone through your code and boils down to one thing. The way that the component <Switch> works with fragment <></>. It only looks for the first React Fragment because they do not want to transverse a tree:

https://github.com/ReactTraining/react-router/issues/5785

To solve that you need to either remove the React.Fragment inside your components. So your application will look like:

<Switch> 
    <Route ...>
    <Route ...>
    <Route ...>
</Switch>

and NOT (btw that is how it is now)

      <Switch> 
    //it will only sees the first one //if you switch orders - Private with Public
    // Private will work but not Public anymore :(
        <React.Fragment>
            <Route ...>
            <Route ...>
        </React.Fragment>
        <React.Fragment>
            <Route ...>
        </React.Fragment>
     </Switch>

Another solution (that is what I did because I am not well versed in TypeScript enough to change types and returns) is to add a wrapper in your switch application and deal with the return of the private Routes using the render method inside the <Route> as demonstrated below:

  //index.tsx
        <Switch>
            <>
            <PublicRoute publicRoutes={publicRoutes}/>
            <PrivateRoute privateRoutes={privateRoutes} isLogin={login}/>
            </>
        </Switch>

That leads to another error of infinite loops re-renders (again the react-router is probably having a bad time with the nested routes) and to solve that you would do the following to your PrivateRoutes component:

  //PrivateRoute.tsx
return (
    <>
        {props.privateRoutes.map(item =>
            <Route key={item.path} exact path={item.path} render={() => (
                !props.isLogin
                    ? (
                    <Redirect to={PUBLIC.path}/>
                ):
                    //HOC transforming function Component into Component
                    // @ts-ignore (you can deal here better than me hehehe)
                    ((PrivateComponent)=><PrivateComponent/>)(item.component)
            )}/>)}
    </>
);

TL,DR: You are adding nesting complexity by adding <></> (translates to React.Fragment) inside your structure. If you remove them or follow the code above you should be fine

Hope I have helped it you. Good luck! :)

iwaduarte
  • 1,078
  • 16
  • 18
  • 2
    Hi there! Thanks so much! After your answer I literally realized it's just a simple question :( It really helps. You are right, too many `<>>` messed up routes. I actually just put those two `Routes` maps under `` instead of creating two components. Everything solved. Thanks again! – WhyINeedToKnowThis Aug 29 '19 at 06:08