30

Here is my validation schema:

const validationSchema = Yup.object().shape({
      person: Yup.object().shape({
        name: Yup.string().required('Field is required'),
        surname: Yup.string().required('Field is required'),
        middleName: Yup.string().required('Field is required'),
        email: Yup.string()
          .email('Wrong e-mail format')
          .required('Field is required')
      }),
      company: Yup.object().shape({
        name: Yup.string().required('Field is required'),
        address: Yup.string().required('Field is required'),
        email: Yup.string()
          .email('Wrong e-mail format')
          .required('Field is required')
      })
    });

And also there are two variables in React State: isPerson and isCompany. How to make validation work conditionally, for example if isPerson is true, then person in validationSchema is required to be validated?

Sgt Maddonut
  • 759
  • 2
  • 8
  • 20

5 Answers5

69

Updated ans: 2023.

you can use Yup conditions

const validationSchema = Yup.object().shape({

      isCompany: Yup.boolean(),
      companyName: Yup.string().when('isCompany', {
        is: true,
        then: Yup.string().required('Field is required')
      }),
      companyAddress: Yup.string().when('isCompany', {
        is: (isCompany) => true,//just an e.g. you can return a function
        then: Yup.string().required('Field is required'),
        otherwise: Yup.string()
      }),
      // test an unconventional type
      logo: Yup.mixed()
       .test('file_size', 'error: file size exceeds!', (files, context) => {
         return filesIsValid(files) ? files[0].size <= 10000 : true
    /** with Input file, target will be a FileList,
    you can handle onChange at your form level
    // [Please note]: Here finally I'm returning `true`,
    // you can use context?.createError() but it will void .isNotReuired() 
    // meaning even if you keep this field optional your form will be still invalid 
    **/
       })
       .test('file_type', 'error msg: file type must match', (files, context) => {// test and return bool or contextError})
    });


And make sure to update your form accordingly. I hope you get the point...

STEEL
  • 8,955
  • 9
  • 67
  • 89
  • 19
    This is a nice standard conditional validation where the conditions are defined between form fields. However, the original question was how to make a similar condition but based on state, i. e. without having `isCompany` in the schema (and the form). – dmudro Apr 19 '20 at 21:56
  • just a correction: the param of the function is 'isCompany', not 'companyValue' – deathemperor Jun 03 '20 at 10:38
  • 2
    When use multiples values, example `is: 1 || 2`, it's not working. – cura May 13 '21 at 17:44
  • 1
    @cura try doing ```is: isCompany => isCompany === 1 || isCompany === 2``` – Embedded_Mugs Nov 26 '22 at 23:38
30

You can conditionally add to your validation schema just like any other object:

let validationShape = {
  company: Yup.object().shape({
    name: Yup.string().required('Field is required'),
    address: Yup.string().required('Field is required'),
    email: Yup.string()
      .email('Wrong e-mail format')
      .required('Field is required')
  })
};

if (this.state.isPerson) {
  validationShape.person = Yup.object().shape({
    name: Yup.string().required('Field is required'),
    surname: Yup.string().required('Field is required'),
    middleName: Yup.string().required('Field is required'),
    email: Yup.string()
      .email('Wrong e-mail format')
      .required('Field is required');
}

const validationSchema = Yup.object().shape(validationShape);
imjared
  • 19,492
  • 4
  • 49
  • 72
16

Conditional validations with YUP :

All kinds of validation that can be done with when are as follows:

1. Single value, simple condition :
RULE: Only ask for personName when isPerson true.

personName : Yup.string().when('isPerson', {
    is: true, 
    then: Yup.string().required('Field is required'),
    otherwise: Yup.string(),
})
// alternatively, using callback
personName : Yup.string().when('isPerson', ([isPerson], schema) => {
    if (isPerson) 
        return Yup.string().required('Field is required');
    return schema;
})

2. Single value, complex condition :
RULE: Only ask for personName when company is "IT".

personName : Yup.string().when('company', {
    is: (company) => company === 'IT', 
    then: Yup.string().required('Field is required'),
    otherwise: Yup.string(),
})
// alternatively, using callback
personName : Yup.string().when('company', ([company], schema) => {
    if (company === 'IT') 
        return Yup.string().required('Field is required');
    return schema;
})

3. Multi value, complex condition :
RULE: Only ask for personName when company is "IT" and person is valid.

personName : Yup.string().when(['company', 'isPerson'], {
    is: (company, isPerson) => company === 'IT' && isPerson, 
    then: Yup.string().required('Field is required'),
    otherwise: Yup.string(),
})
// alternatively, using callback
personName : Yup.string().when(['company', 'isPerson'], ([company, isPerson], schema) => {
    if (company === 'IT' && isPerson) 
        return Yup.string().required('Field is required');
    return schema;
})
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
restricted-beam
  • 1,528
  • 2
  • 4
  • 8
  • 1
    This answer should've more upvotes. It covers all the scenarios which one can encounter in the formik conditional validations. – Su So Apr 17 '23 at 14:47
4

While the accepted solution works, it had one problem - two of the fields to be validated were common, and had to be duplicated. In my case, I had majority of the fields common with just 2-4 outliers.

So here is another solution:

  1. Define each schema separately - i.e. 3 schemas - commonSchema for the common fields, personSchema for person specfic fields & companySchema for company specific fields.

  2. Merge the schemas based on the state

     const validationSchema = isPerson 
                            ? commonSchema.concat(personSchema)
                            : commonSchema.concat(companySchema)
    

For details on "concat", refer to the yup docs on github.

Adnan Erkocevic
  • 111
  • 2
  • 13
2
                email: Yup.string()
                    .when(['showEmail', 'anotherField'], {
                        is: (showEmail, anotherField) => {
                            return (showEmail && anotherField);
                        },
                        then: Yup.string().required('Must enter email address')
                    }),

Multiple fields can also be used for validation.

HudsonGraeme
  • 399
  • 3
  • 10
pareshm
  • 4,874
  • 5
  • 35
  • 53