Problems with react router trying to redirect users

Viewed 42

i'm working on a MERNG app, and i have a problem loging out the user.

So, i have a route, that will take you to a page where the url has /profile/userId, the userID is given by the localStorage since i'm taking that value from there, and, when i logout i remove the token from the localStorage, so, if there's no values, it should take you login page.

Now, the problem is that, when i login, and get to the /profile/userId page, and i refresh the page with f5, if i logout, it won't let me do it, the redirecting doesn't work, it only works if i don't refresh the page, and that's a big issue for my app, and it's actually weird.

Maybe it's a problem with my code, i don't know that well how to use Redirect, so if you can help me with this, you're the greatest !

So this is the code

Routes with the redirect

import React from "react";
import "./App.css";

import {
  BrowserRouter as Router,
  Switch,
  Route,
  Redirect
} from "react-router-dom";

import Auth from "./website/Auth";
import SocialMedia from "./website/SocialMedia";
import SingleUser from "./website/SingleUser";

function App() {
  const logIn = JSON.parse(localStorage.getItem("token"));

  return (
    <Router>
      <Switch>
        <Route exact path="/">
          {logIn ? <Redirect to={`/profile/${logIn.data.id}`} /> : <Auth />}
        </Route>
        <Route exact path="/socialmedia" component={SocialMedia} />
        <Route exact path="/profile/:id" component={SingleUser} />
      </Switch>
    </Router>
  );
}

export default App;

To logout

import React from "react";

import { useHistory } from "react-router-dom";
import { useDispatch } from "react-redux";

const SingleUser = () => {
  const history = useHistory();
  const dispatch = useDispatch();

  const userData = JSON.parse(localStorage.getItem("token"));

  const logout = () => {
    dispatch({ type: "LOGOUT" });
    if (!userData) {
      history.push("/");
    }
  };

  return <div onClick={logout}>Single User</div>;
};

export default SingleUser;

My reducer where i store the token in the localStorage and remove it with logout action

const reducer = (state = { authData: null }, action) => {
  switch (action.type) {
    case "LOGIN":
      localStorage.setItem("token", JSON.stringify({ data: action.payload }));
      return {
        ...state,
        authData: action.payload
      };

    case "LOGOUT":
      localStorage.removeItem("token");
      return {
        ...state,
        authData: null
      };

    default:
      return state;
  }
};

export default reducer;

3 Answers

This is simply because you are rendering the Auth component only in the "/" route.

So if you are on a different route, you are skipping the "/" route and the user route will be shown. There are different ways on how to handle it:

  1. Always redirect
<Switch>
        <Route exact path="/">
          <Auth />
        </Route>
        {!logIn && <Route path="*"><Redirect to={`/profile/${logIn.data.id}`} /></Route>} // This route will not always be rendered if the user should login and is not on the "/" page
        <Route exact path="/socialmedia" component={SocialMedia} />
        <Route exact path="/profile/:id" component={SingleUser} />
      </Switch>
  1. Redirect manually for every route
const SingleUser = () => {
  const history = useHistory();
  const dispatch = useDispatch();

  const userData = JSON.parse(localStorage.getItem("token"));
  if(!userData) histroy.push("/")
 ...
  1. Early return
function App() {
  const logIn = JSON.parse(localStorage.getItem("token"));
  if(!login) return <Auth />
  return (
    <Router>
      <Switch>
        <Route exact path="/">
          <Redirect to={`/profile/${logIn.data.id}`} />
        </Route>
        <Route exact path="/socialmedia" component={SocialMedia} />
        <Route exact path="/profile/:id" component={SingleUser} />
      </Switch>
    </Router>
  );
}

Additionally, userData will always be defined on logout, since you are not accessing a new lcoalState but the current one, before it got removed. It still lives in your varibale.

if (!userData) { // Remove the if
    history.push("/");
}

Another quite easy solution for you might be just doing this:

<Route exact path="/" render={() => (logIn ? <SingleUser /> : <Auth />)} />

This way on every rerender it will check if there is a user and if not it will redirect you directly to the Auth Component.

Hi mike from the past !

Your code it's okay, but, when you click logout, the variable on the App.js component is still the same, so, you won't get the results you're looking for, what you have to do, it's to reload the website, so te App.js component will upload the variable and will recognize that the value of variable has changed, so you will go to login/register again!

just change your code for this one, so far everything work as expected

import React from "react";

import { useHistory } from "react-router-dom";
import { useDispatch } from "react-redux";

const SingleUser = () => {
  const history = useHistory();
  const dispatch = useDispatch();

  const logout = () => {
    dispatch({ type: "LOGOUT" });

    history.push("/");
    history.go("/");
  };

  return <div onClick={logout}>Single User</div>;
};

export default SingleUser;

Related