Cross-field validation not working
See original GitHub issueI often need validation rules that access more than just a single field. Typical examples are:
- ‘Password’ and ‘Repeat password’: The validation rule on ‘Repeat password’ should not only check that the field is not empty. It should also check that the value is identical to the value in ‘Password’.
- ‘Start time’ and ‘End time’: The validation rule on ‘End time’ should not only check that ‘End time’ is a valid time. It should also check that ‘End time’ is greater than ‘Start time’.
The important thing here is that these validators should not just be re-evaluated when the value of their own field changes. They also have to be re-evaluated when the value of another field that’s used in the validator changes. For example, the validator on ‘Repeat password’ should run whenever ‘Password’ or ‘Repeat password’ is changed.
Luckily, MobX is great at tracking these kinds of dependencies, and it’s my understanding that you use MobX internally. So I expected that I could simply access the values of other fields within a validator. MobX would then automatically detect that the validator for ‘Repeat password’ depends on the value of ‘Password’ and re-evaluate it accordingly.
For some reason, this doesn’t seem to work. The validator on ‘Repeat password’ only gets re-evaluated when I change the value of ‘Repeat password’, not when I change the value of ‘Password’.
Below is my demo code. I’m sure I must have made some small error, but I can’t find it.
import React, { Component } from 'react';
import { FormState, FieldState } from 'formstate';
import { observer } from 'mobx-react';
@observer
class Input extends Component {
render() {
const { fieldState, label } = this.props;
return (
<div>
<p>{label}</p>
<p style={{ color: 'orange' }}>{fieldState.error}</p>
<input
type="text"
value={fieldState.value}
onChange={args => fieldState.onChange(args.target.value)}
/>
</div>
);
}
}
@observer
export default class Usecase extends Component {
constructor(props) {
super(props);
this.formState = new FormState({
userName: new FieldState({ value: '' })
.validators(value => !value && 'User name is required.'),
password: new FieldState({ value: '' })
.validators(value => !value && 'Password is required.'),
repeatPassword: new FieldState({ value: '' })
.validators(value => {
if (!value) return 'Repeated password is required.';
return (value !== this.formState.$.password.$) && 'Repeated password must match password.';
})
});
}
componentWillMount() {
this.formState.validate();
}
render(){
return (
<form>
<Input fieldState={this.formState.$.userName} label="User name" />
<Input fieldState={this.formState.$.password} label="Password" />
<Input fieldState={this.formState.$.repeatPassword} label="Repeat password" />
</form>
);
}
}
Issue Analytics
- State:
- Created 7 years ago
- Comments:7
Top GitHub Comments
I used inheritance to wrap up the functionality I’ve been playing with and to access the protected fields which make things work a little more correctly.
It’s still a horrible hack, but I think it’s finally to a point where I need to say “good enough” and move on.
No. If you call
formstate.compose
, they also run when all the fields are valid see : https://formstate.github.io/demos/#cross-field-validationSorry for the late reply. Been a busy few days / weekend ❤️