Реакция / редукционная форма: как вернуть обещание из onSubmit?

Я пытаюсь понять redux, react-redux и редукс-форма.

Я настроил хранилище и добавил редуктор из редукционной формы. Мой компонент формы выглядит так:

Форма входа

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 в магазин и настроил создателей действий для входа в систему. как описано в редукс-документах для асинхронных действий:

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 я вижу, что это работает, как и ожидалось. Когда dispatch(login(formData)) вызывается в onSubmit в форме входа в систему, сначала отправляется действие LOGIN_REQUEST, а затем LOGIN_SUCCESS или LOGIN_FAILURE. LOGIN_REQUEST добавит свойство state.auth.pending = true в магазин, LOGIN_SUCCESS и LOGIN_FAILURE удалит это свойство. (Я знаю, что это может быть что-то для использования reselect, но сейчас я хочу, чтобы это было просто.

Теперь в документах с редукционной формой я прочитал, что могу вернуть обещание от onSubmit для обновления состояния формы (submitting, error). Но я не уверен, как правильно это сделать. dispatch(login(formData)) возвращает undefined.

Я мог бы заменить флаг state.auth.pending в магазине на переменную типа 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 (который возвращает обещание) на onSubmit, но я хотел бы, чтобы он был отделен от компонента React.

Любые советы по этому поводу?


person x-ray    schedule 08.01.2016    source источник


Ответы (1)


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;
    }
}
person Michelle Tilley    schedule 08.01.2016
comment
Можно ли вернуть обещание на основе ошибок APIClient? - person gkkirsch; 26.01.2016