我正在尝试在我的注册表格上使用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解决方案。
当您想从状态中获取数据时,最简单的方法就是在您真正需要它之前。在这种情况下,只需要在渲染中使用它即可。
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)。
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; }
人们在评论/回答中说错误应该处于状态,有时是正确的。如果没有错误就无法实现渲染,则错误应该处于状态。当您可以通过从状态获取现有数据来实现时,这意味着将其置于状态将是多余的。
冗余的问题在于,它增加了很难发现错误的可能性。一个无法避免将数据保持为状态的示例是异步验证。没有冗余,因为您不能仅从表单输入中得出冗余。
这就是为什么。