14

I need to navigate back to the original requested URL after login.

For example, user enters www.example.com/settings as user is not authenticated, it will navigate to login page www.example.com/login.

Once authenticated, it should navigate back to www.example.com/settings automatically.

My original approach with react-router-dom v5 is quite simple:

const PrivateRoute = ({ isLoggedIn, component: Component, ...rest }) => {
  return (
    <Route
      {...rest}
      render={(props) =>
        isLoggedIn? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{ pathname: `/login/${props.location.search}`, state: { from: props.location } }}
          />
        )
      }
    />
  );
};


<PrivateRoute exact isLoggedIn={isLoggedIn} path="/settings" component={Settings} />

Can some one tell me how to do that in v6? Thanks in advance

Ben Li
  • 203
  • 1
  • 2
  • 5

1 Answers1

9

In react-router-dom v6 rendering routes and handling redirects is quite different than in v5. Gone are custom route components, they are replaced with a wrapper component pattern.

v5 - Custom Route

Takes props and conditionally renders a Route component with the route props passed through or a Redirect component with route state holding the current location.

const CustomRoute = ({ isLoggedIn, ...props }) => {
  const location = useLocation();
  return isLoggedIn? (
    <Route {...props} />
  ) : (
    <Redirect
      to={{
        pathname: `/login/${location.search}`,
        state: { location },
      }}
    />
  );
};

...

<PrivateRoute
  exact
  isLoggedIn={isLoggedIn}
  path="/settings"
  component={Settings}
/>

v6 - Custom Wrapper

Takes props and conditionally renders an Outlet component for nested Route components to be rendered into or a Navigate component with route state holding the current location.

const CustomWrapper = ({ isLoggedIn, ...props }) => {
  const location = useLocation();
  return isLoggedIn? (
    <Outlet />
  ) : (
    <Navigate
      to={`/login/${location.search}`}
      replace
      state={{ location }}
    />
  )
};

...

<Route path="settings" element={<CustomWrapper isLoggedIn={isLoggedIn} />} >
  <Route path="settings" element={<Settings />} />
</Route>
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • The advantage of using a query variable is that state will be maintained in a refresh. Nonetheless your approach is good. – Njuguna Mureithi Dec 15 '21 at 05:41
  • 1
    @NjugunaMureithi I don't quite follow your comment, but it's the established v6 pattern. See [auth example](https://reactrouter.com/docs/en/v6/examples/auth). – Drew Reese Dec 15 '21 at 05:42
  • 1
    PS. Your answer is the correct one. Let me clarify my question. I mean: Would your answer work if I was at the login page and refreshed the page from my browser? Ie. would the redirect/navigate happen? – Njuguna Mureithi Dec 15 '21 at 05:50
  • @NjugunaMureithi That depends entirely on if the component rendered on "/login" or whatever the path is renders a redirect or offers any other sort of imperative redirect. It's often the case that the login component will be placed on a sort of "anti-auth" route, meaning if you are already authenticated it will redirect you out. If you are on the login page and refreshed the page I'd expect to stay on the login page since you didn't already get bounced out the first time. – Drew Reese Dec 15 '21 at 05:56
  • @NjugunaMureithi Ah, I see what you mean now... you are asking "Is the route state maintained while on the login page and reload the page?" No, not by default. If this is a use case to be concerned with then the login page should cache the redirect referrer so it can be eventually redirect back to the original route being accessed. – Drew Reese Dec 15 '21 at 05:59
  • I see you get my point. That was why I was using a query parameter. But mine is an edge case. OP should be ok with you solution. – Njuguna Mureithi Dec 15 '21 at 06:03