Реагировать/сокращать форму: как вернуть обещание от onSubmit?
Я пытаюсь обернуть голову вокруг redux, реакция-редукция и 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 промежуточное программное обеспечение в хранилище и установил создателей (-ов) действия для входа в систему, как описано в сокращенные документы для 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)))
}
}
Опять же, в redux DevTools я вижу, что это работает так, как ожидалось. Когда dispatch(login(formData))
вызывается в onSubmit
в LoginForm, сначала отправляется действие LOGIN_REQUEST
, а затем LOGIN_SUCCESS
или LOGIN_FAILURE
. LOGIN_REQUEST
добавит свойство state.auth.pending = true
в хранилище, LOGIN_SUCCESS
и LOGIN_FAILURE
удалит это свойство. (Я знаю, что это может мне что-то использовать reselect, но на данный момент я хочу, чтобы это было просто.
Теперь в документах redux-form я прочитал, что могу вернуть обещание от 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.
Любые советы по этому поводу?
Ответы
Ответ 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;
}
}