Reputation: 457
I am trying to build a password confirmation form in React but I can't get the input validation to work both ways. To reproduce:
The last step does not work and I think there's something wrong with the way I'm setting the password state in handlePasswordInput
. this.state.password
in the isConfirmedPassword
method does not hold the latest password when calling this method.
I've created a bin with the code: https://jsbin.com/sufupedayi/edit?js,output
Any React experts who can point me in the right direction?
SignIn = React.createClass({
getInitialState() {
return {
password: null,
confirmPassword: null
}
},
handlePasswordInput(value) {
if (!_.isEmpty(this.state.confirmPassword)) {
this.refs.confirmPassword.validate(value);
}
this.setState({
password: value
})
},
handleConfirmPasswordInput(value) {
this.setState({
confirmPassword: value
})
},
isConfirmedPassword(value) {
return (value === this.state.password)
},
render() {
return (
<form autoComplete="off">
<Input
name="password"
placeholder="Password"
errorMessage="Password is required"
onChange={this.handlePasswordInput}
/>
<Input
ref="confirmPassword"
name="confirmPassword"
placeholder="Confirm password"
errorMessage="Passwords do not match"
onChange={this.handleConfirmPasswordInput}
validate={this.isConfirmedPassword}
/>
<button type="submit">Submit</button>
</form>
)
}
});
Input = React.createClass({
getInitialState() {
return {
valid: false,
value: null,
errorMessage: this.props.errorMessage,
errorVisible: false
}
},
handleChange(event) {
this.setState({
value: event.target.value
})
if (this.props.validate) {
this.validate(event.target.value);
}
if (this.props.onChange) {
this.props.onChange(event.target.value);
}
},
validate(value) {
if (this.props.validate && this.props.validate(value)) {
this.setState({
valid: true,
errorVisible: false
});
} else {
this.setState({
valid: false,
errorVisible: true
});
}
},
render() {
return (
<div>
<input
name={this.props.name}
placeholder={this.props.placeholder}
onChange={this.handleChange}
/>
{!this.state.valid && <InputError errorMessage={this.state.errorMessage} visible={this.state.errorVisible} />}
</div>
)
}
});
InputError = React.createClass({
render() {
var divStyle = {
display: this.props.visible ? 'inline-block': 'none'
}
return (
<div style={divStyle}>{this.props.errorMessage}</div>
)
}
})
React.render(<SignIn />, document.body);
Upvotes: 2
Views: 7053
Reputation: 66425
I think you're making this more complicated than it needs to be and so you are already running into trouble with race conditions and hard to follow code. Try this instead:
var SignIn = React.createClass({
getInitialState() {
return {
password: null,
valid: false
}
},
checkRequired(value) {
return !!value.trim();
},
checkPasswordsMatch(value) {
var match = this.refs.password.getValue() === value;
this.setState({
valid: match,
password: value
});
return match;
},
render() {
return (
<form autoComplete="off">
<Input
ref="password"
name="password"
placeholder="Password"
errorMessage="Password is required"
validate={this.checkRequired}
/>
<Input
name="confirmPassword"
placeholder="Confirm password"
errorMessage="Passwords do not match"
validate={this.checkPasswordsMatch}
/>
<button type="submit">Submit</button>
</form>
)
}
});
var Input = React.createClass({
getInitialState() {
return {
hasChanged: false,
valid: false
}
},
handleChange(event) {
if (this.props.validate) {
this.setState({
valid: this.props.validate(event.target.value)
});
}
this.setState({
hasChanged: true
});
},
getValue() {
//return this.refs.input.value; // For React 0.14+
return this.refs.input.getDOMNode().value;
},
render() {
return (
<div>
<input
ref="input"
name={this.props.name}
placeholder={this.props.placeholder}
onChange={this.handleChange}
/>
{this.state.hasChanged && !this.state.valid && <InputError errorMessage={this.props.errorMessage} />}
</div>
)
}
});
var InputError = React.createClass({
render() {
return (
<div>{this.props.errorMessage}</div>
)
}
})
e.g. https://jsbin.com/febagojayo/edit?js,output
Upvotes: 0
Reputation: 9422
Replace your handlePasswordInput with this. The reason it was happening was, your handlePasswordInput was being called before password was updated in the state. This does not happen when you were typing the confirm password field, because the password was already updated in the state.
Working fiddle - https://jsbin.com/cipuguxezi/1/edit?html,js,output
handlePasswordInput(value) {
this.setState({
password: value
});
var self= this;
window.setTimeout(function(){
if (self.state.confirmPassword && self.state.confirmPassword.length) {
self.refs.confirmPassword.validate(self.state.confirmPassword);
}
});
}
Note: There is some bug in JSBIN, please make sure
<div id="app"/>
is present above the script tag.
Upvotes: 2