react / redux-form: comment rendre la promesse d'onSubmit?

je suis en train d'envelopper ma tête autour de redux,réagissent-redux et redux-forme.

j'ai configuré un magasin et j'ai ajouté le réducteur de redux-form. Ma composante de forme ressemble à ceci:

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)

Cela fonctionne comme prévu, dans redux DevTools je peux voir comment le magasin est mis à jour sur le formulaire d'entrée et sur la présentation de la forme login action créateur dépêches les actions de connexion.

j'ai ajouté redux-thunk middleware au magasin et configurer le(s) créateur (s) d'action pour se connecter comme décrit dans le redux docs pour des Actions Asynchrones:

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

encore une fois, dans redux DevTools je peux voir que cela fonctionne comme prévu. Lorsque dispatch(login(formData)) est appelé onSubmit dans le LoginForm, d'abord le LOGIN_REQUEST action est distribué, suivi par LOGIN_SUCCESS ou LOGIN_FAILURE. LOGIN_REQUEST ajouter une propriété state.auth.pending = true pour le magasin, LOGIN_SUCCESS et LOGIN_FAILURE supprimer cette propriété. (Je sais que cela pourrait être quelque chose d'utiliser resélectionner pour, mais pour l'instant je veux rester simple.

maintenant, dans les docs redux-form j'ai lu que je peux retourner une promesse de onSubmit mettre à jour l'état du formulaire (submitting,error). Mais je ne suis pas sûr quelle est la manière correcte de le faire. dispatch(login(formData)) retourne undefined.

j'ai pu échanger le state.auth.pending drapeau dans le magasin avec une variable comme state.auth.status avec les valeurs demande,succès et échec (et encore une fois, je pourrais probablement utiliser reselect ou quelque chose de semblable pour cela).

je ne puis souscrire à la stocker dans onSubmit et gérer les modifications apportées à state.auth.status comme ceci:

// ...

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
}

// ...

cependant, cette solution ne se sent pas bien et je ne suis pas sûr qu'elle fonctionnera toujours comme prévu lorsque l'application se développe et que d'autres actions pourraient être lancées à partir d'autres sources.

une autre solution que j'ai vue est de déplacer l'appel api (qui renvoie une promesse) vers onSubmit, mais je voudrais le garder séparé du composant React.

des conseils à ce sujet?

15
demandé sur x-ray 2016-01-08 22:56:59

1 réponses

dispatch(login(formData)) retourne undefined

basé sur les docs pour redux-thunk:

toute valeur de retour de la fonction interne sera disponible comme valeur de retour de dispatch lui-même.

Donc, vous voulez quelque chose comme

// 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
répondu Michelle Tilley 2016-01-08 22:17:04