web-dev-qa-db-ja.com

react / redux-form:onSubmitからpromiseを返す方法は?

reduxreact-reduxredux-form に頭を包み込もうとしています。

ストアをセットアップし、redux-formからレデューサーを追加しました。私のフォームコンポーネントは次のようになります。

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)
_

これは期待どおりに機能します。 redux DevTools フォームの入力時、およびフォームの送信時にストアがどのように更新されるかを確認できます。loginアクション作成者がログインアクションをディスパッチします。

redux-thunk ミドルウェアをストアに追加し、 非同期アクションのreduxドキュメント で説明されているように、ログインするためのアクション作成者をセットアップしました。

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のonSubmitdispatch(login(formData))が呼び出されると、最初に_LOGIN_REQUEST_アクションがディスパッチされ、次に_LOGIN_SUCCESS_または_LOGIN_FAILURE_がディスパッチされます。 _LOGIN_REQUEST_はプロパティ_state.auth.pending = true_をストアに追加し、_LOGIN_SUCCESS_と_LOGIN_FAILURE_はこのプロパティを削除します。 (私はこれが私に何かを使うかもしれないことを知っています 再選択 のために、しかし今のところ私はそれを単純に保ちたいです。

さて、redux-formのドキュメントで、onSubmitからpromiseを返して、フォームの状態(submittingerror)を更新できることを読みました。しかし、これを行う正しい方法が何であるかはわかりません。 dispatch(login(formData))undefinedを返します。

ストア内の_state.auth.pending_フラグを、値requestedsuccess、およびfailureの_state.auth.status_などの変数と交換できます。 =(繰り返しになりますが、おそらくこれには再選択などを使用できます)。

次に、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呼び出し(promiseを返す)をonSubmitに移動することですが、Reactコンポーネントから分離したままにしておきたいです。

これについて何かアドバイスはありますか?

15
x-ray

dispatch(login(formData))undefinedを返します

redux-thunkのドキュメント に基づく:

内部関数からの戻り値は、dispatch自体の戻り値として使用できます。

だから、あなたは次のようなものが欲しいでしょう

// 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;
    }
}
13
Michelle Tilley