Conditional validation for fields based on select option - yup

2.9k views Asked by At

So while learning, forms i found this package - yup for validating. But i am getting a problem

So i will explain the problem statement and what i have tried so far.

So the use case is i have a select field, which has two options, Resident and Non-Resident, so based on the selection i have different fields which i render

  • Resident : first_name, last_name
  • Non-Resident: passport_number, country, state

So here what i thought of splitting the schema and based on the selection i want to add the additional fields schema for the validation.

const required = () => "Field is required";

const resident_schema = object({
  first_name: string().required(required).nullable(),
  last_name: string().required(required).nullable(),
});

const non_resident_schema = object({
  passport_number: string().required(required).nullable(),
  country: object().required(required).nullable(),
  state: object().required(required).nullable(),
});

const schemaBasedCitizen = {
  RESIDENT: resident_schema,
  NON-RESIDENT: non_resident_schema,
};

export const validationSchema = object().shape({
  citizen: object().required(required).nullable(),
  { ...object().when("citizen", {
    is: (value) => !!schemaBasedCitizen[value.toUpperCase()],
    then: (value) => schemaBasedCitizen[value.toUpperCase()],
  }) },
});

And my html consist of

<select>
 <option disabled selected value> -- select an option -- </option>
 <option value='resident'>Resident</option>
 <option value='non-resident'>Non-Resident</option>
</select>

I am not able to spread, since key requires, So is there a way to achieve this.

Update

Schema when value is resident in select field

export const validationSchema = object().shape({
      citizen: object().required(required).nullable(),
      first_name: string().required(required).nullable(),
      last_name: string().required(required).nullable(),
    });

Schema when value is non-resident in select field

export const validationSchema = object().shape({
      citizen: object().required(required).nullable(),
      passport_number: string().required(required).nullable(),
      country: object().required(required).nullable(),
      state: object().required(required).nullable(),
    });

Basically the validation is dynamic based on the value on citizen select field.

1

There are 1 answers

8
Adam Jenkins On BEST ANSWER

yup has this thing that it can only reference sibling fields (or context) using when.

Your citizen field is NOT a sibling of the rest of the object and can't be used to conditionally change the object schema, it's a child of the object, so you can't reference it as a sibling field via when.

One way to accomplish what you want could be to use a lazy schema:

import { lazy } from 'yup';

const residentSchema = ...
const nonResidentSchema = ...

export const validationSchema = lazy(({citizen}) => citizen === 'resident' ? residentSchema : nonResidentSchema);