当状态未立即更新时,ReactJS表单验证 [英] ReactJS form validation when state is not immediately updated

查看:73
本文介绍了当状态未立即更新时,ReactJS表单验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用ReactJS在我的注册表单上创建客户端验证。我正在使用 http://validatejs.org/ 库进行验证以及 https://github.com/jhudson8/react-semantic-ui 用于渲染语义-ui React组件的组件。这是代码。

I am trying to create client side validation with ReactJS on my registration form. I am using http://validatejs.org/ library for validations along with https://github.com/jhudson8/react-semantic-ui components for rendering semantic-ui React components. Here is the code.

var constraints = {
  email: {
    presence: true, 
    email:true
  }, 
  password: {
    presence: true,
    length: { minimum: 5 }
  }
}

var RegistrationForm = React.createClass({

  getInitialState: function() {
    return { 
      data: {},
      errors: {}
    };
  },

  changeState: function () {
    this.setState({
      data: {
        email: this.refs.email.getDOMNode().value,
        password: this.refs.password.getDOMNode().value,
        password_confirmation:  this.refs.password_confirmation.getDOMNode().value
      }
    });
    console.log("State after update");
    console.log(this.state.data);
  },

  handleChange: function(e) {
    this.changeState();
    var validation_errors = validate(this.state.data, constraints);

    if(validation_errors){
      console.log(validation_errors);
      this.setState({errors: validation_errors});
    }
    else
      this.setState({errors: {}});
  },

  handleSubmit: function(e) {
    e.preventDefault();
    //code left out..
  },

  render: function() {
    var Control = rsui.form.Control;
    var Form = rsui.form.Form;
    var Text = rsui.input.Text;
    var Button = rsui.form.Button;
    return (
      <Form onSubmit={this.handleSubmit} onChange={this.handleChange}>
        <h4 className="ui dividing header">New User Registration</h4>
        <Control label="Email" error={this.state.errors.email}>
          <Text name="email" type="email" ref="email"  key="email" value={this.state.data.email}></Text>
        </Control>
        <Control label="Password" error={this.state.errors.password}>
          <Text name="password" type="password" ref="password" key="password" value={this.state.data.password}></Text>
        </Control>
        <Control label="Password Confirmation">
          <Text name="password_confirmation" type="password" ref="password_confirmation" key="password_confirmation" value={this.state.data.password_confirmation}></Text>
        </Control>
        <Button> Register </Button>
      </Form>);
  }
});

我遇到的问题是,当我调用this.setState时,状态不会立即更新,因此,当我调用validate(this.state.data,constraints)时,我正在验证之前的状态,因此用户的UI体验变得奇怪,例如:

The problem I am having is that when I call this.setState, the state is not immediately updated, so when I call validate(this.state.data, constraints) I am validating previous state, so user's UI experience gets weird, for example:

如果我有'示例@em'在我的电子邮件字段中,我输入'a',它将验证字符串'example @ em'而不是'example @ ema',所以实质上它总是验证新键击之前的状态。我必须在这里做一些根本错误的事情。我知道组件的状态不会立即更新,只有在渲染完成后才会更新。

If I have 'example@em' in my email field and I enter 'a', it will validate string 'example@em' not 'example@ema', so in essence it always validates the state before the new key stroke. I must be doing something fundamentally wrong here. I know state of the component is not updated right away, only after render is done.

我应该在渲染功能中进行验证吗?

Should I be doing validations in render function ?

---解决方案---

--- SOLUTION ---

像Felix Kling一样向setState添加回调建议解决了这个问题。以下是带有解决方案的更新代码:

Adding a callback to setState like Felix Kling suggested solved it. Here is the updated code with solution:

var RegistrationForm = React.createClass({

  getInitialState: function() {
    return { 
      data: {},
      errors: {}
    };
  },

  changeState: function () {
    this.setState({
      data: {
        email: this.refs.email.getDOMNode().value,
        password: this.refs.password.getDOMNode().value,
        password_confirmation: this.refs.password_confirmation.getDOMNode().value
      }
    },this.validate);
  },

  validate: function () {
    console.log(this.state.data);
    var validation_errors = validate(this.state.data, constraints);

    if(validation_errors){
      console.log(validation_errors);
      this.setState({errors: validation_errors});
    }
    else
      this.setState({errors: {}});
  },

  handleChange: function(e) {
    console.log('handle change fired');
    this.changeState();
  },

  handleSubmit: function(e) {
    e.preventDefault();
    console.log(this.state);
  },

  render: function() {
    var Control = rsui.form.Control;
    var Form = rsui.form.Form;
    var Text = rsui.input.Text;
    var Button = rsui.form.Button;
    return (
      <Form onSubmit={this.handleSubmit} onChange={this.handleChange}>
        <h4 className="ui dividing header">New Rowing Club Registration</h4>
        <Control label="Email" error={this.state.errors.email}>
          <Text name="email" type="email" ref="email"  key="email" value={this.state.data.email}></Text>
        </Control>
        <Control label="Password" error={this.state.errors.password}>
          <Text name="password" type="password" ref="password" key="password" value={this.state.data.password}></Text>
        </Control>
        <Control label="Password Confirmation">
          <Text name="password_confirmation" type="password" ref="password_confirmation" key="password_confirmation" value={this.state.data.password_confirmation}></Text>
        </Control>
        <Button> Register </Button>
      </Form>);
  }
});

---更好的解决方案-----

--- BETTER SOLUTION -----

请参阅下面的FakeRainBrigand解决方案。

See FakeRainBrigand's solution below.

推荐答案

当您想从州获取数据时,最简单的方法就是在您真正需要它之前。在这种情况下,你只需要渲染。

When you want to derive data from state, the simplest way to do it is right before you actually need it. In this case, you just need it in render.

validate: function (data) {
  var validation_errors = validate(data, constraints);

  if(validation_errors){
    return validation_errors;
  }

  return {};
},

render: function() {
    var errors = this.validate(this.state.data);
    ...
      <Control label="Email" error={errors.email}>
        ...

状态应该很少用作派生数据缓存。如果您确实想在设置状态时派生数据,请务必小心,并将其设为实例属性(例如 this.errors )。

State should very rarely be used as a derived data cache. If you do want to derive data when setting state, be very careful, and just make it an instance property (e.g. this.errors).

因为setState回调实际上会导致额外的渲染周期,所以你可以不变地更新数据,并将它传递给this.validate(看看我如何进行验证不依赖于this.state.data的当前值在上面的代码?)。

Because the setState callback actually causes an additional render cycle, you can immutably update data instead, and pass it to this.validate (see how I made validate not depend on the current value of this.state.data in the above code?).

根据你当前的changeState,它看起来像这样:

Based on your current changeState, it'd look like this:

changeState: function () {
  var update = React.addons.update;
  var getValue = function(ref){ return this.refs[ref].getDOMNode().value }.bind(this);

  var data = update(this.state.data, {
      email: {$set: getValue('email')},
      password: {$set: getValue('password')},
      password_confirmation: {$set: getValue('password_confirmation')}
   });

   this.errors = this.validate(data);
   this.setState({data: data});
 },

 // we'll implement this because now it's essentially free 
 shouldComponentUpdate: function(nextProps, nextState){
   return this.state.data !== nextState.data;
 }






在评论/答案中人们说错误应该处于状态,这有时是正确的。如果在没有错误处于状态的情况下无法实现渲染,则它们应处于状态。当您可以通过从状态派生现有数据来实现时,这意味着将其置于状态将是多余的。


In the comments/answers people are saying that errors should be in state, and that's sometimes true. When you can't implement render without the errors being in state, they should be in state. When you can implement by deriving existing data from state, that means that putting it in state would be redundant.

冗余问题是增加了很难追踪错误的可能性。您无法避免将数据保持为状态的示例是使用异步验证。没有冗余,因为你无法从表单输入中获得它。

The problem with redundancy is it increases the likelihood of very difficult to track down bugs. An example of where you can't avoid keeping the data as state is with async validation. There's no redundancy, because you can't derive that from just the form inputs.


我错误地没有更新错误状态。
- blushrt

这正是原因。

这篇关于当状态未立即更新时,ReactJS表单验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆