16

I'm new to react-router (and client side routing in general) so I might be thinking about this all wrong. Sorry in advance if that is the case...

Basically just want to implement 3 simple rules:

  • if no user, redirect to '/login'
  • else if route doesn't exist, redirect to '/' (root)
  • else let user go to requested route

I keep track of the user in this.state.user. My current router seems to follow the first 2 rules, but only lets the authenticated user see the home page ('/profile' redirects to '/') so I know I'm doing something wrong but can't figure out what.

 <Router>
    {this.state.user ? (
      <Switch>
        <Route path="/" exact component={Home}/>
        <Route path="/profile" exact component={Profile}/>
        <Route render={() => (<Redirect to="/" />)}/>
      </Switch>
    ) : (
      <Switch>
        <Route path="/login" exact component={Login}/>
        <Route render={() => (<Redirect to="/login" />)}/>
      </Switch>
    )}
 </Router>

Any advice is appreciated. Thank you

GG.
  • 21,083
  • 14
  • 84
  • 130
Egor
  • 764
  • 2
  • 6
  • 11
  • Can you simply try to redirect to root (which is Home) component in case route does not found? – Jaydeep May 15 '18 at 02:44
  • 1
    I tried the same code in code sandbox and the /profile is accessed as expected. Could you please check the link https://codesandbox.io/s/n422y545pl – Rahil Ahmad May 15 '18 at 02:56
  • 1
    You don't need to add Redirect component in the Switch. You have to set a conditional rendering in each component that you want the user to login I.e. You check if the user is logged to view a route, if no. You then render the Redirect component provided by react-router-dom instead. – Bello Mayowa May 15 '18 at 03:06
  • @RahilAhmad thanks for the example. Made me realize that the routing happens before the user state is set. – Egor May 15 '18 at 16:09

3 Answers3

43

For anybody arriving here looking for how to redirect if none of the routes matches:

<Switch>
  // ... your routes
  <Route render={() => <Redirect to="/" />} />
</Switch>

Note that the routes have to be direct children of the <Switch>, e.g. this doesn't work:

<Switch>
  <Fragment>
    // ... your routes
    <Route render={() => <Redirect to="/" />} />
  </Fragment>
</Switch>

(maybe fixed in more recent versions of react-router)

GG.
  • 21,083
  • 14
  • 84
  • 130
11

The answer is simple

<Switch>
  <Route path="/login" exact component={Login}/>
  {!this.state.user && <Redirect to='/login' />}
  <Route path="/" exact component={Home}/>
  <Route path="/profile" exact component={Profile}/>
  <Redirect to="/" />
</Switch>

The main difference between switch and router is that router will try to execute all matched path and append content together, switch will stop on the first match.

My app has a similar approach, but I wrapped protected routed on a separate file, then wrap the user profile as HOC

export const App = () => (
  <Switch>
    <Route exact path='/login' component={Login} />
    {!hasToken() && <Redirect to='/login' />}
    <Route path='/' component={ProtectedRoute} />
  </Switch>
)

protectedRoute.js

const ProtectedRoute = ({ userInfo }: Props) => (
  <Layout userInfo={userInfo}>
    <Switch>
      <Route exact path='/a' component={A} />
      <Route exact path='/b' component={B} />
      <Route path='/c' component={C} />
      <Redirect to='/a' />
    </Switch>
  </Layout>
)

export default withUserInfo(ProtectedRoute)
Mark Lai
  • 121
  • 1
  • 6
  • 1
    "The main difference between switch and router is that router will try to execute all matched path and append content together, switch will stop on the first match." This statement has helped me prevent countless hours of hair pulling. – PsyGik Jun 07 '19 at 11:50
2

Have you thought about using a Route wrapper that checks for a user when a user is necessary for the Route?

const CanHasRouteAccess = ({ component: Component, iHasUser, ...rest }) => {
  return iHasUser ? (
    <Route {...rest} render={props => <Component {...props} />} />
  ) : (
    <Redirect to="/" />
  );
};

You could pass the props to the Route or cause a redirect to the home page when there isn't a user.

<CanHasRouteAccess
  path="/personal-data"
  exact
  component={Profile}
  iHasUser={Boolean(user)}
  />
rxgx
  • 5,089
  • 2
  • 35
  • 43
  • Thanks Ryan, that makes sense and works. Any idea how to best deal with the case of no matched route using redirect to '/' ? – Egor May 15 '18 at 16:19
  • @Egor If you use a `Route` without a path at the bottom of your `Switch`, it will catch all unmatched routes. For example, ``. [Source](https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md) – rxgx May 15 '18 at 23:12