2016-01-08 16 views
14

Sto cercando di avvolgere la mia testa intorno a redux, react-redux e redux-form.react/redux-form: come restituire la promessa da onSubmit?

Ho installato un negozio e aggiunto il riduttore da redux-form. La mia componente forma simile a questa:

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) 

Questo funziona come previsto, in redux DevTools posso vedere come il negozio viene aggiornato su input forma e su presentando il modulo del login azione creatore invia l' azioni di accesso.

ho aggiunto il redux-thunk middleware al negozio e configurazione creatore azione (s) per l'accesso come descritto nelle 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))) 
    } 
} 

Ancora, in DevTools Redux Vedo che funziona come previsto. Quando viene chiamato in onSubmit nel LoginForm, viene inviata prima l'azione LOGIN_REQUEST seguita da LOGIN_SUCCESS o LOGIN_FAILURE. LOGIN_REQUEST aggiungerà una proprietà state.auth.pending = true al negozio, LOGIN_SUCCESS e LOGIN_FAILURE rimuoverà questa proprietà. (So ​​che questo potrebbe essere qualcosa da usare reselect per, ma per ora voglio mantenerlo semplice

Ora, nei documenti redux-form ho letto che posso restituire una promessa da onSubmit per aggiornare lo stato del modulo (submitting, error). Ma non sono sicuro di quello che è il modo corretto per farlo. dispatch(login(formData)) rendimenti undefined.

ho potuto scambiare la bandiera state.auth.pending nel negozio con una variabile come state.auth.status con i valori richiesti, successo e errore (e di nuovo, Probabilmente potrei usare la riselezione o qualcosa di simile per questo).

ho potuto quindi abbonarsi al negozio in onSubmit e gestire le modifiche al state.auth.status in questo modo:

// ... 

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 
} 

// ... 

Tuttavia, questa soluzione non si sente bene e non sono sicuro se sarà sempre funzionare come previsto quando l'app cresce e altre azioni potrebbero essere inviate da altre fonti.

Un'altra soluzione che ho visto è spostare la chiamata api (che restituisce una promessa) a onSubmit, ma vorrei mantenerla separata dal componente React.

Qualche consiglio in merito?

risposta

11

dispatch(login(formData)) rendimenti undefined

Sulla base the docs for redux-thunk:

Qualsiasi valore restituito dalla funzione interna sarà disponibile come valore di ritorno di dispatch stessa.

Quindi, che ci si vuole qualcosa di simile

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

Nizza, grazie! Sapevo che doveva essere più facile ... :) –

+1

È possibile restituire una promessa basata sugli errori apiClient? – gkkirsch