Home > Enterprise >  React router <Redirect> is not redirecting on state
React router <Redirect> is not redirecting on state

Time:11-18

The app should redirect to /dashboard when user is not null. User is null in the first render but useEffect() should pick up and store user data from local storage. Then, the ternary operator should trigger <Redirect to='/dashboard' /> and redirects to dashboard page. But, it's not doing that. It just keeps showing the login page at /login whenever I refresh even if there is data in localStorage.

What am I doing wrong or how can I solve this?

const App = () => {
    const [user, setUser] = useState(null)

    useEffect(() => {
        const loggedUserJSON = window.localStorage.getItem('loggedUser')

        if (loggedUserJSON) {
            const user = JSON.parse(loggedUserJSON)
            setUser(user)
            noteService.setToken(user.token)
        }
    }, [])

    return (
        <Router>
            <Switch>
                <Route exact path='/'>
                    {user
                        ? <Redirect to='/dashboard' />
                        : <Redirect to='/login' />
                    }
                </Route>
                <Route exact path='/login'>
                    <LogIn />
                </Route>
                <Route exact path='/dashboard'>
                    <Dashboard />
                </Route>
            </Switch>
        </Router>
    )

CodePudding user response:

You are not doing it the common way. Try this out :

create PrivateRoute.js

import { Redirect, Route } from "react-router-dom";

 const PrivateRoute = ({user, ...rest}) => {
   return (
     <Route
       {...rest}
       render={(props) =>
         user ? (
               <Redirect to='/dashboard' {..props}/>
         ) : (
           <Redirect to="/login" {..props} />
         )
       }
     />
   );
 };

 export default PrivateRoute;
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

Import it and do so :

const App = () => {
    const [user, setUser] = useState(null)

    useEffect(() => {
        const loggedUserJSON = window.localStorage.getItem('loggedUser')

        if (loggedUserJSON) {
            const user = JSON.parse(loggedUserJSON)
            setUser(user)
            noteService.setToken(user.token)
        }
    }, [])

    return (
        <Router>
            <Switch>
                <PrivateRoute exact path='/' user= {user}/>
                <Route exact path='/login'>
                    <LogIn />
                </Route>
                <Route exact path='/dashboard'>
                    <Dashboard />
                </Route>
            </Switch>
        </Router>
    )
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

Issue

The issue is that on the initial render the user state is null and the redirect to "/login" occurs. When you reload the page, if the path is still "/login" it doesn't exactly match "/" and no redirect occurs, regardless of user state.

Solution

Initialize state from localStorage instead of waiting a render cycle. This at least allows you to check the user state on the first render instead of on the second (or later) rerender. Reorder the routes in the Switch component to specify them from more specific to less specific. The redirect should come last so other paths have a chance to be matched and rendered first.

const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));

const App = () => {
  const [user] = useState(initializeState());

  useEffect(() => {
    noteService.setToken(user.token);
  }, [user]);

  return (
    <Router>
      <Switch>
        <Route path='/login'>
          <LogIn />
        </Route>
        <Route path='/dashboard'>
          <Dashboard />
        </Route>
        <Redirect to={user ? '/dashboard' : '/login'} />
      </Switch>
    </Router>
  );
};

This doesn't quite resolve the page reload issue where a user is already on a sub-route. For this you should implement a ProtectedRoute component and pass the user state as a prop. If there is an user object it renders a regular Route component and passes the props on through, otherwise it renders a Redirect and bounces the user to the login route.

const ProtectedRoute = ({ user, ...props }) => user ? (
  <Route {...props} />
) : (
  <Redirect to="/login" />
);

const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));

const App = () => {
  const [user] = useState(initializeState());

  useEffect(() => {
    noteService.setToken(user.token);
  }, [user]);

  return (
    <Router>
      <Switch>
        <Route path='/login'>
          <LogIn />
        </Route>
        <ProtectedRoute path='/dashboard' user={user} >
          <Dashboard />
        </ProtectedRoute>
        <Redirect to={user ? '/dashboard' : '/login'} />
      </Switch>
    </Router>
  );
};
  • Related