1

I'm using joi-browser for form validation in React. Problem which I'm facing is to validate password and confirmPassword on input.

The schema I'm using is same which is provided in another stackoverflow question.

password: Joi.string().min(3).max(15).required(),
confirmPassword: Joi.any().valid(Joi.ref('password')).required().options({ language: { any: { allowOnly: 'must match password' } } })

Once I start typing in confirmPassword, my error comes even it is same as password and on submit it disappear and submit form. but if confirmPassword is not same it fails to submit that is ok.

What I want is, it should validate on onChange event, and if confirmPassword field matches password field the error must be removed.

Code logic is, all fields are defined in formData in the state, and all errors will be stored in errors object with same name in the state.errors. If error comes, it create entry in errors object else remove it from errors object.

My form state is:

state = {
    formData: {
        password: '',
        confirmPassword: ''
    },
    errors: {}
}

onChange method is:

onChangeField = ({ currentTarget: input }) => {
    const errors = { ...this.state.errors };
    const errorMsg = this.validateField(input);
    if (errorMsg) errors[input.name] = errorMsg;
    else delete errors[input.name];

    const formData = { ...this.state.formData };
    formData[input.name] = input.value;
    this.setState({ formData, errors });
}
validateField = ({ name, value }) => {
    const obj = { [name]: value };
    const schema = { [name]: this.schema[name] };
    const { error } = Joi.validate(obj, schema);
    return error ? error.details[0].message : null;
};
Faisal Janjua
  • 703
  • 1
  • 8
  • 15

2 Answers2

0

I was facing the same issue. It is all because of the async and await mode. If you would have added logging to the case, you would see that it is not setting the state onChangeField. E.g. it is doing this async. I haven't figure it out the best case, but if you add a full validation and wait for the state to be set, it works.

E.g. I added something like:

onChangeField = async ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMsg = this.validateField(input);
if (errorMsg) errors[input.name] = errorMsg;
else delete errors[input.name];

const formData = { ...this.state.formData };
formData[input.name] = input.value;

await this.setState({ data, errors });


if(!this.validate()){
    console.log(true);
    this.setState({ errors: {} });
}

}

Raitis Lebedevs
  • 47
  • 1
  • 13
  • You don't need to use async/await as setState has second perimeter which is callback function and which will execute after setting new state. setState({ data, errors } , ()=>{ console.log('State updated', this.state) }); – Faisal Janjua Oct 02 '20 at 06:27
  • Yes true, but you still need to add the `if(!this.validate()){ console.log(true); this.setState({ errors: {} });` otherwise it didn't function for me, and for this one a call back function needs to be added as well, so it seems to me easier with async and await. Unless you have it done differently, if so, would you mind sharing? – Raitis Lebedevs Oct 02 '20 at 11:21
  • I found many limitations in `Joi` also few custom requirements in my forms, like if Field A is checked make Field B optional/required etc. and In Joi its is really hard to create those kind of strange checks. I started using another Library `Yup` which has same kind of functionality but with extended features. I never found any bug/limitation from this library. it has same pattern as Joi have but it has huge documentation. Here you can get started https://codechips.me/svelte-form-validation-with-yup/ – Faisal Janjua Oct 03 '20 at 18:29
0

The problem is in validateField method specifically in Joi schema used when validating the field confirmPassword. In this schema, it is necessary to add a validation rule for password field, too. So u need to change validateField method to something like this:

validateField = ({ name, value }) => {
 if (name === "confirmPassword") {
      const { formData} = this.state;
      const obj = { password: formData.password, [name]: value };
      const schema = {
        [name]: this.schema[name],
        password: this.schema["newPassword"],
      };
      const { error } = Joi.validate(obj, schema);
      return error ? error.details[0].message : null;
    }else{
     const obj = { [name]: value };
    const schema = { [name]: this.schema[name] };
    const { error } = Joi.validate(obj, schema);
    return error ? error.details[0].message : null;
}
};
Nemanja
  • 3,295
  • 11
  • 15