小编典典

状态未立即更新时的ReactJS表单验证

reactjs

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

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体验变得很奇怪。

如果我在电子邮件字段中输入“ example @ em”,然后输入“ a”,它将验证字符串“ example @ em”而不是“ example @
ema”,因此从本质上讲,它始终在新按键之前验证状态。我一定在这里做一些根本错误的事情。我知道仅在渲染完成后,组件的状态不会立即更新。

我应该在render函数中进行验证吗?

-解决方案-

像Felix Kling一样,向setState添加回调建议解决该问题。这是解决方案的更新代码:

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>);
  }
});

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

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


阅读 260

收藏
2020-07-22

共1个答案

小编典典

当您想从状态中获取数据时,最简单的方法就是在您真正需要它之前。在这种情况下,只需要在渲染中使用它即可。

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)。

因为setState回调实际上会导致额外的渲染周期,所以您可以不变地更新数据,然后将其传递给this.validate(请参见上面代码中如何使validate不依赖this.state.data的当前值?)。

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

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;
 }

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

冗余的问题在于,它增加了很难发现错误的可能性。一个无法避免将数据保持为状态的示例是异步验证。没有冗余,因为您不能仅从表单输入中得出冗余。

这就是为什么。

2020-07-22