简体   繁体   中英

React router authenticated route is redirecting when I refresh the page

My problem is simple, at least it seems. I have state in my redux store that's holding the state of whether the user is logged in or not. That is all working fine, but when the user refreshes the page, for a moment while the authenticated state is async getting it's data, the render runs and state is undefined.

Since, state is undefined, my redirect to /login runs, so the refresh kicks me out of the app and back to login, which then checks to see if I'm already logged in and take me to the homepage.

Any ideas on how to resolve this:

  {
    !this.props.authenticated && (
      <Switch>
        <Route path="/login" component={LoginForm} />
        <Route path="/register" component={RegisterForm} />
        <Route path="" render={props => {
            return <Redirect to="/login" />
          }}
        />
      </Switch>
    )
  }

So, when this.props.authenticated is false for that short period of time, it hits the login redirect. But, a few ms later, this.props.authenticated is true and since the user is already logged in, is redirected to the home route.

Any ideas?

Ideally you wouldn't render your route straight away but wait until your authentication request has resolved and you have a clear state.

Something like this:

 class App extends React.Component { constructor( props ) { super( props ); this.state = { // You could just check if authenticated is null, // but I think having an extra state makes is more clear isLoading: true, authenticated: null, }; this.checkAuthentication(); } checkAuthentication() { // Some async stuff checking against server // I'll simulate an async call with this setTimeout setTimeout( () => this.setState( { authenticated: Boolean( Math.round( Math.random() ) ), isLoading: false, } ), 1000 ); } render() { // Render a loading screen when we don't have a clear state yet if ( this.state.isLoading ) { return <div>loading</div>; } // Otherwise it is safe to render our routes return ( <div> routes,<br /> random authenticated: <strong> { this.state.authenticated.toString() } </strong> </div> ); } } ReactDOM.render( ( <App /> ), document.querySelector( 'main' ) ); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <main></main> 

Okay, lumio helped get me on the right track with setTimeout, so instead I worked it out with async/await:

class App extends Component {
  state = {
    error: "",
    isLoading: true,
  }

  async componentDidMount() {
    let token = localStorage.getItem('jwtToken');
    if (token) {
      setAuthToken(token);
      await this.props.isAuthenticatedAction(true);
    } else {
      await this.props.isAuthenticatedAction(false);
    }
    this.setState({
      isLoading: false,
    });
  }

  handleLogout = (evt) => {
    evt.preventDefault();
    localStorage.removeItem('jwtToken');
    window.location.reload();
  }

  render() {
    if (this.state.isLoading) {
      return <div></div>;
    } else {
      // return my regular content
    }

You can use react-router-dom for auth work flow.

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

////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time

const AuthExample = () => (
  <Router>
    <div>
      <AuthButton />
      <ul>
        <li>
          <Link to="/public">Public Page</Link>
        </li>
        <li>
          <Link to="/protected">Protected Page</Link>
        </li>
      </ul>
      <Route path="/public" component={Public} />
      <Route path="/login" component={Login} />
      <PrivateRoute path="/protected" component={Protected} />
    </div>
  </Router>
);

const fakeAuth = {
  isAuthenticated: false,
  authenticate(cb) {
    this.isAuthenticated = true;
    setTimeout(cb, 100); // fake async
  },
  signout(cb) {
    this.isAuthenticated = false;
    setTimeout(cb, 100);
  }
};

const AuthButton = withRouter(
  ({ history }) =>
    fakeAuth.isAuthenticated ? (
      <p>
        Welcome!{" "}
        <button
          onClick={() => {
            fakeAuth.signout(() => history.push("/"));
          }}
        >
          Sign out
        </button>
      </p>
    ) : (
      <p>You are not logged in.</p>
    )
);

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      fakeAuth.isAuthenticated ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);

const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;

class Login extends React.Component {
  state = {
    redirectToReferrer: false
  };

  login = () => {
    fakeAuth.authenticate(() => {
      this.setState({ redirectToReferrer: true });
    });
  };

  render() {
    const { from } = this.props.location.state || { from: { pathname: "/" } };
    const { redirectToReferrer } = this.state;

    if (redirectToReferrer) {
      return <Redirect to={from} />;
    }

    return (
      <div>
        <p>You must log in to view the page at {from.pathname}</p>
        <button onClick={this.login}>Log in</button>
      </div>
    );
  }
}

export default AuthExample;

refer link https://reacttraining.com/react-router/web/example/auth-workflow

First of all when user try to login then you receive token in response when user is authenticated. now you have to store token in localStorage using

if(user.token){
   localStorage.setItem('user', JSON.stringify(user));
}

it is indicated that when you have token in localstorage you are login otherwise you are logout.

now try to set state to redirect to home page if you want to go home page after login.

this.setState({redirectToReferrer: true});

now return redirect to desire page

if (this.state.redirectToReferrer){
    return (<Redirect to={'/home'}/>)
    }

login.js

import React from 'react';
import axios from 'axios';
import {Redirect} from 'react-router-dom';

export default class Login extends React.Component{
    constructor(props){
        super(props);
        this.state = {
           email : '' ,
           password : '',
           redirectToReferrer : false
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }



    handleChange(event){
            this.setState({
                [event.target.name] : event.target.value

            });
    }
    handleSubmit(event){
            event.preventDefault();

            const user = {
            email : this.state.email,
            password : this.state.password
            };

      if(this.state.email && this.state.password)      
        {
     axios.post(`{Api}/login`,user)
     .then((response) =>
        { 
            let userresponse = response;
            console.log(userresponse.data);
            if(userresponse.token){
            sessionStorage.setItem('data',JSON.stringify(userresponse));
            this.setState({redirectToReferrer: true});
            }

        },this)
        .catch((error) => alert(error))

    }
}

render(){
    if (this.state.redirectToReferrer){

        return (<Redirect to={'/user'}/>)
        }
        if (sessionStorage.getItem('data')){

            return (<Redirect to={'/user'}/>)
            }
    return(

        <div>
            <form ref="formdemo" onSubmit={this.handleSubmit}>
            <label>
                 Username:
                <input type="email" name="email" onChange={this.handleChange} placeholder="Enter Your EmailID" required/></label><br/>
                <label>
                Password : 
                <input type="password" name="password" onChange={this.handleChange} placeholder="Enter Your Password" required/></label><br/>
                <input type="submit"/>
                </form>
        </div>
    )
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM