使用 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 =&gt; Promise.all([ response, response.json()] ))?请注意,它将[response, json] 传递给下一个 .then.. 如果您正在做一些明显不同的事情,那么这将解释未定义的错误(这不是具体的 redux-form 错误)。 @geoboy,我很难让更简单的格式工作,就像.then(() =&gt; , errors =&gt; ),如下所示:github.com/erikras/redux-form/issues/256#issuecomment-157103662。下次我实施它时我会修改这个答案:) @geoboy 不确定这是否相关,并且应该为我构成一个新问题的基础,但我发现如果我的代码有一个简单的语法错误 after “承诺已成功解决”,然后该错误被.catch() 捕获,这不方便地丢失了堆栈跟踪:(仍在努力理解魔法:)【参考方案2】:

原来there are undocumented propertyreturnRejectedSubmitPromise必须设置为true。

【讨论】:

我还不清楚什么时候需要这样做;我不需要它使用我的答案中的实现

以上是关于使用 redux-form 和 Fetch API 进行服务器验证的主要内容,如果未能解决你的问题,请参考以下文章

使用来自 redux-forms "onSubmit" 的 dispatch 分离 API 调用

使用 Redux-Form 的值返回 null

Redux-Form 初始值来自

手动设置 redux-form 字段和/或表单错误

redux-form的学习笔记

redux-form的学习笔记