Gratis Devs
Gratis Devs

Reputation: 165

Correct way to redirect user to different routes based on authentication in ReactJS

In my ReactJS app, routes are configured in below way:

class App extends React.Component{
  constructor(props){
    super(props);
    this.state={
      isLoggedin: false
    }
    
  }
  componentDidMount(){
    if(localStorage.getItem('name'))
    {
      this.setState({
        isLoggedin: true
      })}
  }
  render(){
    return(
      <>
      
      <Switch>
        <Route exact path="/" render={()=><Login isLoggedin={this.state.isLoggedin} />} />
        <Route exact path="/login" render={<Login isLoggedin={this.state.isLoggedin} />} />
        <Route exact path="/home" render={()=><Home isLoggedin={this.state.isLoggedin} />} />
        
      </Switch></>
      
    );
  }
}

In Login.js:

class Login extends React.Component{
    render(){
        if(this.props.isLoggedin) return <Redirect to="/home" />;
        return(
            <h1>Login here</h1>
        );
    }
}

In Home.js:

class Home extends React.Component{
    render(){
        if(!this.props.isLoggedin) return <Redirect to="/login" />;
        return(
            <h1>Home</h1>
        );
    }
}

So what this code will do is that when the user visits the /, it would first go to Login component and as soon as isLoggedin is set to true, it would redirect the user to Home.js. Same thing would happen if user is not loggedin and he tries to access /home, he would be redirected to /login. Since I am using local storage, all of this would happen in flash of eye. It is also working just fine.

But I doubt if it is the best method to achieve my goal. I want to know if there is any more advisable method to do this.

Thanks!!

Upvotes: 1

Views: 1458

Answers (1)

Drew Reese
Drew Reese

Reputation: 202686

A more advisable method would be to decouple the auth checks from the components and abstract this into custom route components.

PrivateRoute - if the user is authenticated then a regular Route is rendered and the props are passed through, otherwise redirect to the "/login" path for user to authenticate.

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

AnonymousRoute - Basically the inverse of the private route. If the user is already authenticated then redirect them to the "/home" path, otherwise render a route and pass the route props through.

const AnonymousRoute = ({ isLoggedIn, ...props }) => {
  return isLoggedIn ? <Redirect to="/home" /> : <Route {...props} />;
};

From here you render the Login and Home components into their respective custom route components.

<Switch>
  <PrivateRoute
    isLoggedIn={this.state.isLoggedIn} // *
    path="/home"
    component={Home}
  />
  <AnonymousRoute
    isLoggedIn={this.state.isLoggedIn} // *
    path={["/login", "/"]}
    component={Login}
  />
</Switch>

Edit correct-way-to-redirect-user-to-different-routes-based-on-authentication-in-reac

* NOTE: The isLoggedIn={this.state.isLoggedIn} prop is only required here since the isLoggedIn state resides in the App component. A typical React application would store the auth state in a React Context or in global state like Redux, and thus wouldn't need to be explicitly passed via props, it could be accessed from within the custom route component.

Full sandbox code:

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

const AnonymousRoute = ({ isLoggedIn, ...props }) => {
  return isLoggedIn ? <Redirect to="/home" /> : <Route {...props} />;
};

class Login extends Component {
  render() {
    return (
      <>
        <h1>Login here</h1>
        <button type="button" onClick={this.props.login}>
          Log in
        </button>
      </>
    );
  }
}

class Home extends Component {
  render() {
    return (
      <>
        <h1>Home</h1>
        <button type="button" onClick={this.props.logout}>
          Log out
        </button>
      </>
    );
  }
}

export default class App extends Component {
  state = {
    isLoggedIn: false
  };

  componentDidMount() {
    if (localStorage.getItem("isLoggedIn")) {
      this.setState({
        isLoggedIn: true
      });
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.isLoggedIn !== this.state.isLoggedIn) {
      localStorage.setItem("isLoggedIn", JSON.stringify(this.state.isLoggedIn));
    }
  }

  logInHandler = () => this.setState({ isLoggedIn: true });
  logOutHandler = () => this.setState({ isLoggedIn: false });

  render() {
    return (
      <div className="App">
        <div>Authenticated: {this.state.isLoggedIn ? "Yes" : "No"}</div>

        <ul>
          <li>
            <Link to="/">/</Link>
          </li>
          <li>
            <Link to="/home">home</Link>
          </li>
          <li>
            <Link to="/login">log in</Link>
          </li>
        </ul>

        <Switch>
          <PrivateRoute
            isLoggedIn={this.state.isLoggedIn}
            path="/home"
            render={() => <Home logout={this.logOutHandler} />}
          />
          <AnonymousRoute
            isLoggedIn={this.state.isLoggedIn}
            path={["/login", "/"]}
            render={() => <Login login={this.logInHandler} />}
          />
        </Switch>
      </div>
    );
  }
}

Upvotes: 1

Related Questions