使用 redux-form 和 Fetch API 进行服务器验证
Posted
技术标签:
【中文标题】使用 redux-form 和 Fetch API 进行服务器验证【英文标题】:Make server validation using redux-form and Fetch API 【发布时间】:2016-03-12 14:14:26 【问题描述】:如何使用redux-form 和 Fetch API 进行服务器端验证? 文档中提供了“Submit Validation”演示,其中说推荐的服务器端验证方法是从 onSubmit 函数返回一个承诺。但是我应该把这个承诺放在哪里呢? 据我了解 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));
如您所见,我使用 fetch API。我预计 fetch 会返回承诺,redux-form 会抓住它,但这不起作用。如何使用来自example 的承诺?
同样从演示中我无法理解 this.props.handleSubmit 函数中应该提供什么。 Demo没有解释这部分,至于我。
【问题讨论】:
这个问题可能会有所帮助:github.com/erikras/redux-form/issues/256 糟糕,我在想github.com/erikras/redux-form/issues/291中的例子 @memeLab 是的,你是对的!谢谢你的链接 @Denis 您对此的最终解决方案是什么? 【参考方案1】:这是我根据http://erikras.github.io/redux-form/#/examples/submit-validation 的示例使用 fetch 的看法。
...但是我应该把承诺放在哪里? ...this.props.handleSubmit 中应该提供什么?
详细信息在下面的 cmets 中;抱歉,代码块需要一些滚动才能阅读:/
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 );
);
);
;
如果我遗漏了什么/被误解等,请与 cmets 联系,我会修改:)
【讨论】:
我认为可能没有必要将 postToApi 包装在另一个承诺中。例如,请参阅 github.com/erikras/redux-form/issues/256#issuecomment-155859342。 感谢您的解决方案。我有一个类似的问题,但是在实现您的解决方案时,即使 API 成功返回,我的代码也会运行到 catch 块中。在 catch 块中返回的错误是"TypeError: Cannot read property 'status' of undefined(…)"
。我不确定这来自 redux-form。关于我可能遗漏的任何想法?另外,对于您的上述评论,您是否建议编辑代码以删除 return new Promise
部分?
@geoboy,在您的 fetch() 包装器中,您是否返回 .then( response => Promise.all([ response, response.json()] ))
?请注意,它将[response, json]
传递给下一个 .then.. 如果您正在做一些明显不同的事情,那么这将解释未定义的错误(这不是具体的 redux-form 错误)。
@geoboy,我很难让更简单的格式工作,就像.then(() => , errors => )
,如下所示:github.com/erikras/redux-form/issues/256#issuecomment-157103662。下次我实施它时我会修改这个答案:)
@geoboy 不确定这是否相关,并且应该为我构成一个新问题的基础,但我发现如果我的代码有一个简单的语法错误 after “承诺已成功解决”,然后该错误被.catch()
捕获,这不方便地丢失了堆栈跟踪:(仍在努力理解魔法:)【参考方案2】:
原来there are undocumented propertyreturnRejectedSubmitPromise
必须设置为true。
【讨论】:
我还不清楚什么时候需要这样做;我不需要它使用我的答案中的实现以上是关于使用 redux-form 和 Fetch API 进行服务器验证的主要内容,如果未能解决你的问题,请参考以下文章