2016-01-08 9 views
14

Próbuję owijać głowę wokół redux, react-redux i redux-form.react/redux-form: jak zwrócić obietnicę z onSubmit?

Mam skonfigurować sklep i dodano reduktor z formularza redux. Mój składnik postać wygląda następująco:

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) 

To działa zgodnie z oczekiwaniami, w redux DevTools widzę, jak sklep jest aktualizowana na wejście formie i na wysłaniu formularza działanie twórca login wywołuje akcje logowania.

I dodaje redux-thunk pośredniej do magazynu i konfiguracja twórcy działanie (S) do rejestrowania, w sposób opisany w 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))) 
    } 
} 

Ponownie w DevTools Redux Widzę, że działa zgodnie z oczekiwaniami. Kiedy dispatch(login(formData)) jest wywoływana w onSubmit w LoginForm, najpierw wywoływana jest akcja LOGIN_REQUEST, a następnie LOGIN_SUCCESS lub LOGIN_FAILURE. LOGIN_REQUEST doda obiekt state.auth.pending = true do sklepu, LOGIN_SUCCESS i LOGIN_FAILURE usunie tę właściwość. (Wiem, że to może być coś do wykorzystania dla reselect dla, ale na razie chcę, aby to było proste

Teraz, w dokumentach w formacie redux czytałem, że mogę zwrócić obietnicę z onSubmit, aby zaktualizować stan formularza (submitting, error). Ale nie jestem pewien, co to jest poprawny sposób to zrobić. dispatch(login(formData)) powraca undefined.

mógłbym wymieniać flagę state.auth.pending w sklepie ze zmienną jak state.auth.status z wartościami żądanych, sukces i niepowodzenie (i ponownie Prawdopodobnie mógłbym użyć ponownego wyboru lub czegoś podobnego do tego).

mogłem potem zapisać się do sklepu w onSubmit i uchwyt zmiany state.auth.status jak ten:

// ... 

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 
} 

// ... 

Jednak to rozwiązanie nie czują się dobrze i nie jestem pewien, czy to będzie zawsze działać zgodnie z oczekiwaniami kiedy aplikacja rośnie, a więcej działań może zostać wysłanych z innych źródeł.

Innym rozwiązaniem, które widziałem, jest przeniesienie wywołania api (które zwraca obietnicę) na onSubmit, ale chciałbym zachować to oddzielenie od komponentu React.

Wszelkie porady na ten temat?

Odpowiedz

11

dispatch(login(formData)) powraca undefined

podstawie the docs for redux-thunk:

Każda wartość powrotna z funkcji wewnętrznej będzie dostępna jako wartość powrotna samej dispatch.

Tak, że chcesz coś podobnego

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

Nicea, dzięki! Wiedziałem, że musi być łatwiej ... :) –

+1

Czy można zwrócić obietnicę na podstawie błędów apiClient? – gkkirsch

Powiązane problemy