redux-form とFetch APIを使用してサーバー側の検証を行う方法は?ドキュメントには「 SubmitValidation 」のデモがあり、サーバー側の検証を行うための推奨される方法は、onSubmit関数からpromiseを返すことです。しかし、私はその約束をどこに置くべきですか?私が理解したように、onSubmit関数は私の行動であるべきです。
<form onSubmit={this.props.addWidget}>...
This.props.addWidgetが実際に私のアクションである場合、以下に示します。
import fetch from 'isomorphic-fetch';
...
function fetchAddWidget(widget, workspace) {
return dispatch => {
dispatch(requestAddWidget(widget, workspace));
return fetch.post(`/service/workspace/${workspace}/widget`, widget)
.then(parseJSON)
.then(json => {
dispatch(successAddWidget(json, workspace));
DataManager.handleSubscribes(json);
})
.catch(error => popupErrorMessages(error));
}
}
export function addWidget(data, workspace) {
return (dispatch, getState) => {
return dispatch(fetchAddWidget(data, workspace));
}
}
ご覧のとおり、フェッチAPIを使用しています。 fetchがpromiseを返し、redux-formがそれをキャッチすることを期待していましたが、それは機能しません。 例 からの約束でそれを機能させる方法は?
また、デモからは、this.props.handleSubmit関数で何を提供する必要があるのか理解できません。私のように、デモはこの部分を説明していません。
http://erikras.github.io/redux-form/#/examples/submit-validation の例に基づいてフェッチを使用する私の見解は次のとおりです。
- ...しかし、私はその約束をどこに置くべきですか?
- ... this.props.handleSubmitで何を提供する必要がありますか?
詳細は以下のコメントにあります。申し訳ありませんが、コードブロックを読み取るには少しスクロールする必要があります:/
components/submitValidation.js
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { myHandleSubmit, show as showResults } from '../redux/modules/submission';
class SubmitValidationForm extends Component {
// the following three props are all provided by the reduxForm() wrapper / decorator
static propTypes = {
// the field names we passed in the wrapper;
// each field is now an object with properties:
// value, error, touched, dirty, etc
// and methods onFocus, onBlur, etc
fields: PropTypes.object.isRequired,
// handleSubmit is _how_ to handle submission:
// eg, preventDefault, validate, etc
// not _what_ constitutes or follows success or fail.. that's up to us
// I must pass a submit function to this form, but I can either:
// a) import or define a function in this component (see above), then:
// `<form onSubmit={ this.props.handleSubmit(myHandleSubmit) }>`, or
// b) pass that function to this component as
// `<SubmitValidationForm onSubmit={ myHandleSubmit } etc />`, then
// `<form onSubmit={this.props.handleSubmit}>`
handleSubmit: PropTypes.func.isRequired,
// redux-form listens for `reject({_error: 'my error'})`, we receive `this.props.error`
error: PropTypes.string
};
render() {
const { fields: { username, password }, error, handleSubmit } = this.props;
return (
<form onSubmit={ handleSubmit(myHandleSubmit) }>
<input type="text" {...username} />
{
// this can be read as "if touched and error, then render div"
username.touched && username.error && <div className="form-error">{ username.error }</div>
}
<input type="password" {...password} />
{ password.touched && password.error && <div className="form-error">{ password.error }</div> }
{
// this is the generic error, passed through as { _error: 'something wrong' }
error && <div className="text-center text-danger">{ error }</div>
}
// not sure why in the example @erikras uses
// `onClick={ handleSubmit }` here.. I suspect a typo.
// because I'm using `type="submit"` this button will trigger onSubmit
<button type="submit">Log In</button>
</form>
);
}
}
// this is the Higher Order Component I've been referring to
// as the wrapper, and it may also be written as a @decorator
export default reduxForm({
form: 'submitValidation',
fields: ['username', 'password'] // we send only field names here
})(SubmitValidationForm);
../redux/modules/submission.js
// (assume appropriate imports)
function postToApi(values) {
return fetch( API_ENDPOINT, {
credentials: 'include',
mode: 'cors',
method: 'post',
body: JSON.stringify({values}),
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': CSRF_TOKEN
}
}).then( response => Promise.all([ response, response.json()] ));
}
export const myHandleSubmit = (values, dispatch) => {
dispatch(startLoading());
return new Promise((resolve, reject) => {
// postToApi is a wrapper around fetch
postToApi(values)
.then(([ response, json ]) => {
dispatch(stopLoading());
// your statuses may be different, I only care about 202 and 400
if (response.status === 202) {
dispatch(showResults(values));
resolve();
}
else if (response.status === 400) {
// here I expect that the server will return the shape:
// {
// username: 'User does not exist',
// password: 'Wrong password',
// _error: 'Login failed!'
// }
reject(json.errors);
}
else {
// we're not sure what happened, but handle it:
// our Error will get passed straight to `.catch()`
throw(new Error('Something went horribly wrong!'));
}
})
.catch( error => {
// Otherwise unhandled server error
dispatch(stopLoading());
reject({ _error: error });
});
});
};
何かを見逃したり、誤解したりした場合は、コメントを添えてチャイムを鳴らしてください。修正します:)
文書化されていないプロパティがありますreturnRejectedSubmitPromise
であることが判明しました。これはtrueに設定する必要があります。