在 React 中异步函数完成执行后调用函数

Call a function after an asynchronous function finishes executing in React

我是 React 的新手,正在尝试通过制作小型、简单的应用程序来学习。我正在制作一个简单的 React 应用程序,它具有 Login 功能。我也在使用 Redux storeRedux-saga。我的登录流程是:

  1. 有一个 Login 组件从用户那里获取 emailpassword,并在单击登录按钮时进行 POST 调用。
  2. emailpassword 被发送到服务器,如果它们有效,服务器 returns 我保存在 local storage 中的响应中的令牌。
  3. 如果收到令牌,则会触发 Login success 的操作。在这里,我设置了一个名为 success: true.
  4. 的标志
  5. 在我的前端,我检查 success 标志的值,如果 success==true 则我重定向到另一个名为 Customers
  6. 的页面

登录组件

import React, { Component } from 'react';
import { connect } from "react-redux";
import { withRouter } from 'react-router-dom';
import { loginRequest } from "../../actions/loginActions";
import './styles.css';

class Login extends Component {
    constructor(props) {
        super(props);
        this.state = {
            email: '',
            password: '',
            error: '',
        };
    }

    dismissError = () => {
        this.setState({ error: '' });
    }

    handleSubmit = (evt) => {
        evt.preventDefault();
        let { email, password } = this.state;
        if (!email) {
            return this.setState({ error: 'Username is required' });
        }
        if (!password) {
            return this.setState({ error: 'Password is required' });
        }

        let data = {
            email: email,
            password: password
        }

        this.props.login(data); //dispatches a method which then makes the POST call
        //the checking happens before the above function has finished executing
        if (this.props.success)
            this.props.history.push('/customers');
        else
            return this.setState({
                error: 'Invalid Username/Password'
            });
    }

    handleChange = (evt) => {
        this.setState({
            [evt.target.name]: evt.target.value
        });
    }

    render() {
        let { email, password } = this.state;
        return ( 
            <form className="loginForm" onSubmit={this.handleSubmit}
                action="/upload">
                <h2>Login</h2>
                {
                    this.state.error &&
                    <h3 className='error' onClick={this.dismissError}>
                        <button onClick={this.dismissError}>✖</button>
                        {this.state.error}
                    </h3>
                }
                <label className="FormFields label">Email</label>
                <input type="email" className="FormFields" name="email" 
                       value={email} 
                       onChange={(event) => this.handleChange(event)} />
                <br />
                <label className="FormFields label">Password</label>
                <input type="password" className="FormFields" name="password" 
                       value={password}
                       onChange={(event) => this.handleChange(event)} />
                <br />
                <input type="submit" className="FormFields submit" 
                       value="Login" />
            </form>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        loading: state.login.loading,
        success: state.login.success
    }
}

const mapDispatchToProps = (dispatch) => {
       return { login: (data) => {dispatch(loginRequest(data))} }
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Login));

登录 Saga

import { put, takeEvery, call } from 'redux-saga/effects'
import { LOGIN_REQUEST, LOGIN_PENDING, LOGIN_SUCCESS, LOGIN_FAILURE } from '../actions/loginActions';

export function* login(action) {
    const { data } = action.payload;
    yield put({ type: LOGIN_PENDING })
    let url = 'myserverurl/login'
    try {
        const response = yield call(fetch, url, {
            method: 'POST',
            body: JSON.stringify(data),
            headers: {
                'Content-Type': 'application/json',
            }
        });
        let tokenObj = yield response.json();
        if (response.status === 200) {
            localStorage.setItem('user', tokenObj.token);
            yield put({ type: LOGIN_SUCCESS, token: tokenObj.token })
        }
    }
    catch (error) {
        yield put({ type: LOGIN_FAILURE, error })
    }
}

export function* watchLogin() {
    yield takeEvery(LOGIN_REQUEST, login)
}

登录减速器非常简单。
登录减速器

import { LOGIN_REQUEST, LOGIN_PENDING, LOGIN_SUCCESS, LOGIN_FAILURE } from '../actions/loginActions';

const initState = {
    loading: false,
    success: false,
    error: ''
}

const loginReducer = (state = initState, action) => {
    switch (action.type) {
        case LOGIN_REQUEST:
            return {
                ...state,
                loading: false
            }
        case LOGIN_PENDING:
            return {
                ...state,
                loading: true
            }
        case LOGIN_SUCCESS:
            return {
                ...state,
                success: true,
                loading: false
            }
        case LOGIN_FAILURE:
            return {
                ...state,
                loading: false,
                success: false,
                error: action.error
            }
        default: return state;
    }
}
export default loginReducer;

登录组件 中的语句 this.props.login(data) 分派操作,然后进行 POST 调用。我想等待上面提到的整个流程完成,然后再检查成功标志的值,但这并没有发生。

在登录的情况下,如何在我的前端检查 success 标志之前等到我的登录减速器的操作完成?我阅读了 async/await 上的文档,但我并不真正理解如何正确使用它们。谁能帮我解决这个问题

您无法在进行异步调用时立即检查 this.props.success,您需要在 getDerivedStateFromProps

中添加对 success 道具的检查

在您的登录组件中添加 getDerivedStateFromProps

static getDerivedStateFromProps(nextProps, prevState) {
   if(!nextProps.loading){
     if(nextProps.success === true) {
       nextProps.history.push('/customers');
     } else {
       return { error: 'Invalid Username/Password' }
     }
   }
   return null
}

从 handleSubmit 中删除以下代码

if (this.props.success)
            this.props.history.push('/customers');
else
 return this.setState({
    error: 'Invalid Username/Password'
 });