我正在尝试使用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)时,我正在验证以前的状态,所以用户&#39; s UI体验变得奇怪,例如:
如果我有&#39;示例@ em&#39;在我的电子邮件字段中输入&#39; a&#39;,它将验证字符串&#39;示例@ em&#39;不是&#39;例如@ ema&#39;,所以本质上它总是在新的键击之前验证状态。我必须在这里做一些根本错误的事情。我知道组件的状态不会立即更新,只有在渲染完成后才会更新。
我应该在渲染功能中进行验证吗?
---解决方案---
像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&#39解决方案。
答案 0 :(得分:6)
如果要从状态派生数据,最简单的方法就是在实际需要之前。在这种情况下,您只需要渲染。
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(请参阅上面代码中我如何验证不依赖于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
这正是原因。
答案 1 :(得分: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
});
},
答案 2 :(得分:0)
一个简单的解决方案是检查changeState
方法中数据的有效性。
另一种解决方案是通过callback to setState
:
此外,您可以提供一个可选的回调函数,该函数在
setState
完成后执行,并重新呈现该组件。[...]
setState()
不会立即改变this.state
,但会创建待处理状态转换。在调用此方法后访问this.state可能会返回现有值。无法保证对
setState
的调用进行同步操作,并且可以对调用进行批处理以获得性能提升。