react / redux-form:如何 return 从 onSubmit 承诺?

react / redux-form: how to return promise from onSubmit?

我正在努力思考 redux, react-redux and redux-form

我已经设置了一个商店并从 redux-form 添加了 reducer。我的表单组件如下所示:

登录表单

import React, {Component, PropTypes} from 'react'
import { reduxForm } from 'redux-form'
import { login } from '../../actions/authActions'

const fields = ['username', 'password'];

class LoginForm extends Component {
    onSubmit (formData, dispatch) {
        dispatch(login(formData))
    }

    render() {
        const {
            fields: { username, password },
            handleSubmit,
            submitting
            } = this.props;

        return (
            <form onSubmit={handleSubmit(this.onSubmit)}>
                <input type="username" placeholder="Username / Email address" {...username} />
                <input type="password" placeholder="Password" {...password} />
                <input type="submit" disabled={submitting} value="Login" />
            </form>
        )
    }
}
LoginForm.propTypes = {
    fields: PropTypes.object.isRequired,
    handleSubmit: PropTypes.func.isRequired,
    submitting: PropTypes.bool.isRequired
}

export default reduxForm({
    form: 'login',
    fields
})(LoginForm)

这按预期工作,在 redux DevTools 我可以看到商店如何在表单输入和提交表单时更新 login 操作创建者调度登录操作。

我添加了 redux-thunk middleware to the store and setup the action creator(s) for logging in as described in the redux docs for Async Actions:

authActions.js

import ApiClient from '../apiClient'

const apiClient = new ApiClient()

export const LOGIN_REQUEST = 'LOGIN_REQUEST'
function requestLogin(credentials) {
    return {
        type: LOGIN_REQUEST,
        credentials
    }
}

export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
function loginSuccess(authToken) {
    return {
        type: LOGIN_SUCCESS,
        authToken
    }
}

export const LOGIN_FAILURE = 'LOGIN_FAILURE'
function loginFailure(error) {
    return {
        type: LOGIN_FAILURE,
        error
    }
}

// thunk action creator returns a function
export function login(credentials) {
    return dispatch => {
        // update app state: requesting login
        dispatch(requestLogin(credentials))

        // try to log in
        apiClient.login(credentials)
            .then(authToken => dispatch(loginSuccess(authToken)))
            .catch(error => dispatch(loginFailure(error)))
    }
}

同样,在 redux DevTools 中,我可以看到这按预期工作。当在 LoginForm 的 onSubmit 中调用 dispatch(login(formData)) 时,首先调度 LOGIN_REQUEST 操作,然后调度 LOGIN_SUCCESSLOGIN_FAILURELOGIN_REQUEST 将向商店添加 属性 state.auth.pending = trueLOGIN_SUCCESSLOGIN_FAILURE 将删除此 属性。 (我知道这可能是我可以使用 reselect 的东西,但现在我想保持简单。

现在,在我读到的 redux-form 文档中,我可以 return 来自 onSubmit 的承诺来更新表单状态(submittingerror)。但我不确定这样做的正确方法是什么。 dispatch(login(formData)) returns undefined.

我可以将商店中的 state.auth.pending 标志与变量 state.auth.status 交换,值 requestedsuccessfailure(同样,我可能会为此使用重新选择或类似的东西)。

然后我可以订阅 onSubmit 中的商店并像这样处理对 state.auth.status 的更改:

// ...

class LoginForm extends Component {
    constructor (props) {
        super(props)
        this.onSubmit = this.onSubmit.bind(this)
    }
    onSubmit (formData, dispatch) {
        const { store } = this.context
        return new Promise((resolve, reject) => {
            const unsubscribe = store.subscribe(() => {
                const state = store.getState()
                const status = state.auth.status

                if (status === 'success' || status === 'failure') {
                    unsubscribe()
                    status === 'success' ? resolve() : reject(state.auth.error)
                }
            })
            dispatch(login(formData))
        }).bind(this)
    }

    // ...
}
// ...
LoginForm.contextTypes = {
    store: PropTypes.object.isRequired
}

// ...

但是,这个解决方案感觉不太好,我不确定它是否会在应用程序增长时始终按预期工作,并且可能会从其他来源调度更多操作。

我看到的另一个解决方案是将 api 调用(return 是一个承诺)移动到 onSubmit,但我想将它与 React 组件分开。

对此有何建议?

dispatch(login(formData)) returns undefined

基于the docs for redux-thunk

Any return value from the inner function will be available as the return value of dispatch itself.

所以,你想要像

这样的东西
// thunk action creator returns a function
export function login(credentials) {
    return dispatch => {
        // update app state: requesting login
        dispatch(requestLogin(credentials))

        // try to log in
        apiClient.login(credentials)
            .then(authToken => dispatch(loginSuccess(authToken)))
            .catch(error => dispatch(loginFailure(error)))

        return promiseOfSomeSort;
    }
}