ReactJS form validation when state is not immediately updated

こ雲淡風輕ζ 提交于 2019-12-03 14:22:28
Brigand

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

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

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?).

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.

I made a mistake of not updating the state of errors too. – blushrt

This is exactly why.

Why wouldn't you just validate the data before you set the state? The errors are also state, so it would be logical to set them in the same fashion as the rest of the state.

changeState: function () {
    var data = {
            email: this.refs.email.getDOMNode().value,
            password: this.refs.password.getDOMNode().value,
            password_confirmation: this.refs.password_confirmation.getDOMNode().value
        },
        errors = validate(data, constraints) || {};

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

A simple solution would be to check the validity of the data in the changeState method instead.

The other solution would be to pass a callback to setState:

In addition, you can supply an optional callback function that is executed once setState is completed and the component is re-rendered.

[...]

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!