Функция render () не работает должным образом в реакции - PullRequest
0 голосов
/ 11 июля 2020

Я использую стек MERN для разработки веб-сайта. Я использовал паспорт. js для локальной аутентификации. Пользователь входит через форму входа на '/login' в веб-интерфейсе. Как только пользователь нажимает кнопку отправки, я предотвращаю его поведение по умолчанию и отправляю запрос на публикацию ax ios на '/login' на бэкэнде. Как только пользователь аутентифицирован, я отправляю его на '/products' во внешнем интерфейсе. Чтобы узнать, аутентифицирован ли пользователь или нет, я отправляю запрос ax ios get на '/login' на бэкэнде в функции compoundDidMount(), которая устанавливает свойство состояния loggedIn на true, если пользователь аутентифицирован, иначе false. Если я еще раз посещу маршрут '/login' во внешнем интерфейсе, я должен быть перенаправлен на '/products' во внешнем интерфейсе без рендеринга кода в операторе else (форма входа) на основе кода в функции render(). Но сначала код в операторе else (форма входа в систему) обрабатывается за долю секунды, а затем он отображает компонент на '/products'. Почему это происходит? Я не хочу, чтобы форма входа отображалась после входа в систему при посещении маршрута '/login' во внешнем интерфейсе. Вот мой код.

login.js (компонент входа в систему)

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

class Login extends Component {
    state = {
        email: "",
        password: "",
        loggedIn: false
    }
    
    handleLogin = event => {
        event.preventDefault();
        const data = {
            email: this.state.email, 
            password: this.state.password
        };
        axios.post('/login', data)
        .then(response => {
            // console.log("response status code: ", response.status);
            if(response.status === 200) {
                // this.setState({loggedIn: true});
                this.props.history.push('/products');
            }
        })
        .catch(error => console.log('error is :', error));
    }

    handleChange = event => {
        const property  = event.target.name,
              value     = event.target.value;
        this.setState({[property]: value});
    }

    componentDidMount() {
        this.getLoggedInStatus();
    }

    getLoggedInStatus = () => {
        axios.get('/login')
        .then(response => {
            console.log("response.data is: ", response.data.loggedIn);
            this.setState({loggedIn: response.data.loggedIn});
            // if(response.data.loggedIn)
            //     this.props.history.push('/products');
        })
        .catch(error => console.log("error is : ", error));
        // return loggedIn;
    }

    render() { 
        // this.getLoggedInStatus();
        // console.log("loggedIn is: ", this.state.loggedIn);
        if(this.state.loggedIn) {
            console.log('Working');
            return <Redirect to="/products" />
        }
        else {
            console.log('not working');
            return (
                <div className="container d-flex justify-content-center mt-5">
                    <form className="w-50 mt-2" action="/login" method="POST" onSubmit={this.handleLogin}>
                        <div className="form-group">
                            <label for="exampleInputEmail1">Email address</label>
                            <input type="email" className="form-control" name="email" onChange={this.handleChange} id="exampleInputEmail1" aria-describedby="emailHelp" required/>
                            <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
                        </div>
                        <div className="form-group">
                            <label for="exampleInputPassword1">Password</label>
                            <input type="password" className="form-control" name="password" onChange={this.handleChange} id="exampleInputPassword1" required/>
                        </div>
                        <button type="submit" className="btn btn-primary">Submit</button>
                    </form>
                </div>
            );
        } 
    }
}
 
export default Login;

код серверной части

router.get('/login', (request, response) => {
    response.json({loggedIn: request.isAuthenticated()});
});

router.post('/login', passport.authenticate('local'), (request, response) => {
    if(request.isAuthenticated())
        // response.json('successful login');
        response.status(200).send({message: 'successful login'});
    else {
        console.log('Not authenticated');
        response.status(404).send({message: 'unsuccessful login'});
    }
});

Ответы [ 3 ]

0 голосов
/ 11 июля 2020

Методы жизненного цикла компонента React вызываются в следующей последовательности:

Конструктор -> componentWillMount -> render -> componentDidMount

Каждый раз, когда вы go переходите на страницу входа * Создается 1005 * NEW экземпляр вашего Login компонента. Ваш «неявный» конструктор инициализирует новый экземпляр с state.loggedIn до false с помощью

state = {
        email: "",
        password: "",
        loggedIn: false
    }

. Это заставляет вашу функцию рендеринга отображать форму входа в систему и вызывать componentDidMount, который отправляет запрос в бэкэнд. Пока ваш компонент ожидает ответа от бэкэнда - на экране отображается форма входа. Когда приходит ответ - вы устанавливаете state.loggedIn в значение true, что вызывает еще один вызов render, и на этот раз ваш компонент перенаправляется на страницу products.

Чтобы решить вашу проблему, вам нужно переместить loggedIn свойство от уровня состояния компонента до уровня состояния приложения, используя что-то вроде redux или любой другой подход для управления состоянием внешнего приложения.

0 голосов
/ 11 июля 2020

Предложение

Добавьте дополнительное свойство (например, загрузка) к вашему объекту состояния, чтобы отслеживать, должен ли логин отображаться или нет (и, возможно, добавление компонента загрузки). См. Также комментарий кода.

Пример:

// Your component state
state = {
        email: "",
        password: "",
        loggedIn: false,
        loading: true // default state
};


// Your getLoggedInStatus code

 getLoggedInStatus = () => {
    axios.get('/login')
    .then(response => {
        console.log("response.data is: ", response.data.loggedIn);
        this.setState({loggedIn: response.data.loggedIn, loading: false}); // update loading state to false
    })
    .catch(error => console.log("error is : ", error));
    // return loggedIn;
}


// Your render code

render() { 
        // this.getLoggedInStatus();
        // console.log("loggedIn is: ", this.state.loggedIn);
        if(this.state.loggedIn) {
            console.log('Working');
            return <Redirect to="/products" />
        }
        else {
            if(this.state.loading) {
               return(<div> Loading...</div>); // Have something more fancy here
            } else {
              return (
                <div className="container d-flex justify-content-center mt-5">
                    <form className="w-50 mt-2" action="/login" method="POST" onSubmit={this.handleLogin}>
                        <div className="form-group">
                            <label for="exampleInputEmail1">Email address</label>
                            <input type="email" className="form-control" name="email" onChange={this.handleChange} id="exampleInputEmail1" aria-describedby="emailHelp" required/>
                            <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
                        </div>
                        <div className="form-group">
                            <label for="exampleInputPassword1">Password</label>
                            <input type="password" className="form-control" name="password" onChange={this.handleChange} id="exampleInputPassword1" required/>
                        </div>
                        <button type="submit" className="btn btn-primary">Submit</button>
                    </form>
                </div>
            );
            }
        } 
    }
0 голосов
/ 11 июля 2020

Возможно, вы захотите использовать состояние isLoading и показывать счетчик во время обработки запроса.

state = {
        email: "",
        password: "",
        loggedIn: false
        idLoading: false
    }

...

getLoggedInStatus = () => {
        this.setState({ isLoading: true });
        axios.get('/login')
        .then(response => {
            console.log("response.data is: ", response.data.loggedIn);
            this.setState({loggedIn: response.data.loggedIn, isLoading: false});
            // if(response.data.loggedIn)
            //     this.props.history.push('/products');
        })
        .catch(error => console.log("error is : ", error));
        // return loggedIn;
    }

...

render() { 
        // this.getLoggedInStatus();
        // console.log("loggedIn is: ", this.state.loggedIn);
        if(this.state.loggedIn) {
            console.log('Working');
            return <Redirect to="/products" />
        }
        else {
            if (this.state.isLoading) {
              <div>Loading...</div>
            }
            return (
                <div className="container d-flex justify-content-center mt-5">
                    <form className="w-50 mt-2" action="/login" method="POST" onSubmit={this.handleLogin}>
                        <div className="form-group">
                            <label for="exampleInputEmail1">Email address</label>
                            <input type="email" className="form-control" name="email" onChange={this.handleChange} id="exampleInputEmail1" aria-describedby="emailHelp" required/>
                            <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
                        </div>
                        <div className="form-group">
                            <label for="exampleInputPassword1">Password</label>
                            <input type="password" className="form-control" name="password" onChange={this.handleChange} id="exampleInputPassword1" required/>
                        </div>
                        <button type="submit" className="btn btn-primary">Submit</button>
                    </form>
                </div>
            );
        } 
    }
...