简体   繁体   English

react / redux-form:如何从onSubmit返回promise?

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

I'm trying to wrap my head around redux , react-redux and redux-form . 我试图围绕reduxreact-reduxredux-form进行包装。

I have setup a store and added the reducer from redux-form. 我已经设置了一个商店并添加了redux-form的reducer。 My form component looks like this: 我的表单组件如下所示:

LoginForm 登录表格

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)

This works as expected, in redux DevTools I can see how the store is updated on form input and on submitting the form the login action creator dispatches the login actions. 这可以正常工作,在redux DevTools中,我可以看到如何在表单输入上更新商店,以及在提交表单时login操作创建者调度登录操作。

I added the redux-thunk middleware to the store and setup the action creator(s) for logging in as described in the redux docs for Async Actions : 我将redux-thunk中间件添加到商店并设置用于登录的操作创建者,如异步操作redux文档中所述:

authActions.js 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)))
    }
}

Again, in redux DevTools I can see that this works as expected. 同样,在redux DevTools中,我可以看到它按预期工作。 When dispatch(login(formData)) is called in onSubmit in the LoginForm, first the LOGIN_REQUEST action is dispatched, followed by LOGIN_SUCCESS or LOGIN_FAILURE . 在LoginForm中的onSubmit中调用dispatch(login(formData)) ,首先调度LOGIN_REQUEST操作,然后调度LOGIN_SUCCESSLOGIN_FAILURE LOGIN_REQUEST will add a property state.auth.pending = true to the store, LOGIN_SUCCESS and LOGIN_FAILURE will remove this property. LOGIN_REQUEST将向商店添加属性state.auth.pending = trueLOGIN_SUCCESSLOGIN_FAILURE将删除此属性。 (I know this might me something to use reselect for, but for now I want to keep it simple. (我知道这可能是我使用重新选择的东西,但是现在我想保持简单。

Now, in the redux-form docs I read that I can return a promise from onSubmit to update the form state ( submitting , error ). 现在,在redux-form文档中,我读到我可以从onSubmit返回一个promise以更新表单状态( submittingerror )。 But I'm not sure what's the correct way to do this. 但我不确定这样做的正确方法是什么。 dispatch(login(formData)) returns undefined . dispatch(login(formData))返回undefined

I could exchange the state.auth.pending flag in the store with a variable like state.auth.status with the values requested , success and failure (and again, I could probably use reselect or something alike for this). 我可以在商店中使用state.auth.status这样的变量交换state.auth.pending标志,其中包含请求的值, 成功失败 (同样,我可能会使用重新选择或类似的东西)。

I could then subscribe to the store in onSubmit and handle changes to state.auth.status like this: 然后我可以订阅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
}

// ...

However, this solution doesn't feel good and I'm not sure if it will always work as expected when the app grows and more actions might be dispatched from other sources. 但是,这个解决方案感觉并不好,我不确定当应用程序增长并且可能会从其他来源发送更多操作时,它是否始终按预期工作。

Another solution I have seen is moving the api call (which returns a promise) to onSubmit , but I would like to keep it seperated from the React component. 我看到的另一个解决方案是将api调用(返回一个promise)移动到onSubmit ,但我想将它与React组件分开。

Any advice on this? 有什么建议吗?

dispatch(login(formData)) returns undefined dispatch(login(formData))返回undefined

Based on the docs for redux-thunk : 基于redux-thunk的文档

Any return value from the inner function will be available as the return value of dispatch itself. 内部函数的任何返回值都将作为dispatch本身的返回值。

So, you'd want something like 所以,你想要的东西

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM