реагировать - реагировать маршрутизатор - privateRoute - бесконечный цикл - PullRequest
0 голосов
/ 23 мая 2018

Я пытаюсь создать простую систему аутентификации в своем одностраничном приложении.Я хочу отключить все маршруты, кроме /login для гостя.Способ узнать, аутентифицирован ли пользователь или гость, это узнать, есть ли access_token в localStorage.

Когда я запускаю приложение, запускается компонент Main.Компонент определяет маршруты и знает, аутентифицирован ли пользователь, проверяя localStorage.

Маршрут по умолчанию (/) предназначен для рендеринга компонента Home, но, как этот пример из реагирующий маршрутизатор компонент Home защищен объектом PrivateRoute.

Объект PrivateRoute проверяет, прошел ли пользователь аутентификацию.Если да, компонент Home отображается, в противном случае пользователь перенаправляется на компонент login в /login.

Компонент Login перенаправляет пользователя на / в случае успеха и выполняетобратный вызов, чтобы дать access_token.

Компонент Main определяет обратный вызов. Он собирается сохранить access_token в localStorage и изменить state для объявления пользователя аутентифицированным.И теперь пользователь может получить доступ к компоненту Home.

Моя проблема в том, что система PrivateRoute всегда проверяет пользователя как гостя, поэтому всегда происходит перенаправление на /login.Но когда access_token in localStorage, компонент Login перенаправляет на Home, защищенный PrivateRoute, и это бесконечный цикл несмотря на обратный вызов handleLogin.

Можете ли вынайти решение?


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>
        );
    }
}

1 Ответ

0 голосов
/ 23 мая 2018

В вашей функции handleSubmit вам нужно вызвать 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 будет иметь правильное значениезначение

...