2015-01-24 3 views
4

Я пытаюсь создать проверку на стороне клиента с помощью ReactJS в моей регистрационной форме. Я использую библиотеку http://validatejs.org/ для валидации вместе с https://github.com/jhudson8/react-semantic-ui компонентами для рендеринга semantic-ui. Реагировать на компоненты. Вот код.Проверка формы ReactJS, когда состояние не обновляется немедленно

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, ограничение) Я проверку предыдущего состояния, так что пользователя Опыт пользовательского интерфейса становится странным, например:

Если у меня есть «пример @ em» в поле электронной почты, и я вхожу в «a», он будет проверять строку «example @ em 'not' example @ ema», поэтому по существу он всегда проверяет состояние перед новым нажатием клавиши. Я должен делать что-то принципиально неправильное здесь. Я знаю, что состояние компонента не обновляется сразу, только после выполнения рендеринга.

Должен ли я выполнять проверки в функции рендеринга?

--- РЕШЕНИЕ ---

Добавление обратного вызова 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 в ниже.

ответ

5

Если вы хотите получить данные из состояния, самый простой способ сделать это прямо перед тем, как он вам действительно понадобится. В этом случае вам просто нужно это сделать в рендеринге.

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 фактически вызывает дополнительный цикл визуализации, вы можете неизменно обновлять данные вместо этого и передавать его этому .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; 
} 

В комментариях/ответы люди говорят, что ошибки должны быть в состоянии, и это иногда верно. Если вы не можете выполнить рендеринг без ошибок, находящихся в состоянии, они должны находиться в состоянии. Когда вы можете реализовать, выведя существующие данные из состояния, это означает, что помещение его в состояние будет избыточным.

Проблема с избыточностью заключается в увеличении вероятности очень сложного отслеживания ошибок. Пример того, где вы не можете избежать сохранения данных в состоянии, является асинхронной проверкой. Нет избыточности, потому что вы не можете получить это только из входов формы.

Я ошибся, не обновляя состояние ошибок. - blushrt

Именно по этой причине.

+0

Спасибо! Это решение имеет намного меньший рендеринг. Я не знал о дополнениях React. Одна вещь, которую я должен был сделать, это добавить функцию .bind (this) в getValue, так что вызов this.refs будет иметь правильный контекст. – blushrt

+0

Для тех, кто использует камень с ребрами, обязательно добавьте config.react.addons = true в application.rb, чтобы вы могли получить доступ к React.addons – blushrt

+0

Но я не согласен с тем, что ошибки формы представляют собой кеш данных данных здесь, они должно быть частью государства. – blushrt

0

Простым решением было бы проверить правильность данных в методе changeState.

Другим решением было бы пропускать callback to setState:

Кроме того, вы можете предоставить дополнительную функцию обратного вызова, который выполняется один раз setState завершен, и компонент повторно оказаны.

[...]

setState() не сразу мутировать this.state но создает отложенный переход состояния. Доступ к this.state после вызова этого метода может потенциально вернуть существующее значение.

Нет гарантии синхронной работы звонков на setState, и звонки могут быть отправлены для повышения производительности.

+0

Проверка правильности данных в changeState не помогла, но добавлен обратный вызов для setState. Благодаря! Я знаю, что это, вероятно, не оптимальное решение, я вижу, что проверка в реакции может быть довольно сложной, я нашел этот отличный ресурс http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation- с-reactjs.html, это, вероятно, будет лучшим решением. – blushrt

+0

@blushrt Почему не проверял достоверность во время changeState? –

+0

@CoryDanielson, я допустил ошибку, не обновляя также состояние ошибок. Так что теперь он должен работать. – blushrt

1

Почему бы вам просто не проверить данные перед установкой состояния? Ошибки также находятся в состоянии, поэтому было бы логично установить их так же, как и остальное состояние.

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 
    }); 
}, 
+0

Извините, моя ошибка, это должно сработать. – blushrt

+0

Я также считаю, что ошибки должны быть частью состояния формы. – blushrt

Смежные вопросы