Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React router v4 redirect when no match

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

like image 673
Egor Avatar asked May 15 '18 01:05

Egor


3 Answers

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)

like image 131
GG. Avatar answered Oct 19 '22 19:10

GG.


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)
like image 40
Mark Lai Avatar answered Oct 19 '22 20:10

Mark Lai


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)}
  />
like image 2
rxgx Avatar answered Oct 19 '22 19:10

rxgx