反应 - 反应路由器 - privateRoute - 无限循环

react - react router - privateRoute - infinite loop

我正在尝试在我的单页应用程序中创建一个简单的身份验证系统。 我想为访客禁用所有路由 execpt /login。 知道用户是否经过身份验证或访客的方法是知道 localStorage.

中是否有 access_token

当我启动应用程序时,Main 组件也会启动。该组件定义路由并通过检查 localStorage.

了解用户是否已通过身份验证

默认路由 (/) 用于渲染 Home 组件,但是,像这样 react routerexample Home 组件受 PrivateRoute 对象保护。

PrivateRoute 对象检查用户是否已通过身份验证。如果是,则呈现 Home 组件,否则用户将被重定向到位于 /login.

login 组件

如果成功,Login 组件将用户重定向到 / 并执行回调以提供 access_token.

Main组件定义了回调,它即将在localStorage中保存access_token并更改state以声明用户已通过身份验证。现在,用户可以访问 Home 组件。

我的问题是,PrivateRoute 系统总是将用户检查为访客,因此它总是重定向到 /login。但是当 localStorage 中的 access_token 时,Login 组件重定向到 PrivateRoute 保护的 Home,尽管 [=40] 这是一个无限循环=]回调。

你能找到解决办法吗?


Main.jsx

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Link, Redirect, Route} from "react-router-dom";
import {Login} from "./Login"
import {Home} from "./Home";


class Main extends Component {
    constructor(props) {
        super(props);

        this.handleLogout = this.handleLogout.bind(this);
        this.handleLogin = this.handleLogin.bind(this);
        this.state = {authed: localStorage.getItem('access_token') !== null};
    }

    componentDidCatch(error, info) {
    }

    handleLogout(event) {
        event.preventDefault();
        localStorage.removeItem('access_token');
        this.setState({authed: false});
    }

    handleLogin(token) {
        localStorage.setItem('access_token', token);
        this.setState({authed: token !== null});
    }

    render() {
        const PrivateRoute = ({component: Component, ...rest}) => (
            <Route {...rest} render={props =>
                this.state.authed()
                    ? (<Component {...props} />)
                    : (<Redirect to="/login"/>)
            }
            />
        );

        const LoginLogout = () => {
            return this.state.authed
                ? (<button onClick={this.handleLogout}>Logout</button>)
                : (<Link to="/login">Login</Link>);
        };

        return (
            <BrowserRouter>
                <div>
                    <ul>
                        <li>
                            <Link to="/">Home</Link>
                        </li>
                        <li>
                            <LoginLogout/>
                        </li>
                    </ul>

                    <Route path="/login" component={() => <Login handleLogin={this.handleLogin}/>}/>
                    <PrivateRoute exact path="/" component={Home}/>

                </div>
            </BrowserRouter>
        );
    }
}

if (document.getElementById('main')) {
    ReactDOM.render(<Main/>, document.getElementById('main'));
}

Login.jsx

import React, {Component} from 'react';
import {Redirect} from "react-router-dom";

export class Login extends Component {

    constructor(props) {
        super(props);

        this.state = {
            email: '',
            password: '',
            redirect: localStorage.getItem('access_token') !== null,
            token: null,
            loading: false,
            error: null
        };
        this.handleInputChange = this.handleInputChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    componentWillUnmount() {
        this.props.handleLogin(this.state.token);
    }

    handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;

        this.setState({
            [name]: value
        });
    }

    handleSubmit(event) {
        event.preventDefault();
        this.setState({
            error: null,
            loading: true
        });
        axios.post('/api/login', {
            'client_id': '3',
            'email': this.state.email,
            'password': this.state.password,
            'confirm_password': this.state.password
        }).then((response) => {
            let token = response.data.access_token;
            this.setState({
                redirect: true,
                token: token,
                loading: false
            });
        }, (error) => {
            console.error('error', error.response.data);
            this.setState({
                error: error.response.data,
                loading: false
            });
        });
    }

    render() {

        if (this.state.redirect)
            return (<Redirect to={"/"}/>);

        return (
            <form onSubmit={this.handleSubmit}>
                <label htmlFor="email">Email :</label>
                <input type="text" name="email" id="email" value={this.state.email} onChange={this.handleInputChange}
                       disabled={this.state.loading}/>
                <label htmlFor="password">Password :</label>
                <input type="password" name="password" id="password" value={this.state.password}
                       onChange={this.handleInputChange} disabled={this.state.loading}/>
                <button type="submit"
                        disabled={this.state.loading}>{this.state.loading ? "..." : "Se connecter"}</button>
                {this.state.error && (
                    <div>
                        <p>Erreur : {JSON.stringify(this.state.error)}</p>
                    </div>
                )}
            </form>
        );
    }
}

在您的 handleSubmit 函数中,您需要从 props 调用 handleLogin 以便容器组件中的状态正确更新。

 handleSubmit(event) {
...

.then((response) => {
            let token = response.data.access_token;
            this.setState({
                redirect: true,
                token: token,
                loading: false
            });
           // here, call the handle from props
           this.props.handleLogin(token);
        }

...

这样,您的 this.state.authed 就会有正确的值