yup validation hierarchy issue (formik vs react-hook-form with yupResolver)

Viewed 23

I am working on converting forms from Formik over to React Hook Form. yup is the validator in both cases. However I've noticed that despite leaving the validation schema in tact, there seems to be some sort of hierarchy mismatch between Formik and RHF.

I noticed for instance that for min, max, required, originally in that order, validation was working correctly with Formik, but I had to invoke required before min & max in order for the correct validation message to trigger on an input. That fix was self-explanatory enough.

But some of these are tricky. For instance:

    dob: yup.date().when([], (schema: yup.DateSchema) => {
      return boolCheck1 && boolCheck2
        ? yup.string()
        : schema
            .min(
              moment.utc().add(1, 'day').subtract(126, 'years').toDate(),
              'an error message'
            )
            .max(
              moment.utc().subtract(15, 'years').toDate(),
              'another error message'
            )
            .transform((_, originalValue) => {
              const date = moment.utc(originalValue, 'MM/DD/YYYY');
              return date.isValid()
                ? date.startOf('day').toDate()
                : new Date('');
            })
            .typeError('A valid date is required (MM/DD/YYYY)')
            .required('Please enter a valid date');
   })

It seems that regardless of where I am placing the required check, the one that is triggered is typeError ("A valid date is required (MM/DD/YYYY)"). Basically, if a user touches and blurs this input field without having typed anything, it should trigger the required message ("Please enter a valid date".) Only when a user types something that does not pass the typeError check should the typeError message appear.

This was working previously with Formik. However, now, I'm using React Hook Form as such:

  const rhfMethods = useForm({
    defaultValues,
    resolver: yupResolver(validationSchema),
    mode: 'onTouched',
  });

The only difference really is that now the yupResolver is being used (without which I get a typescript error.)

If anyone could shed any light on this that would be greatly appreciated!

1 Answers

Nvm, looks like a similar question had already been asked and answered.

While that definitely helped shed light on the issue ('' is still a string type, not simply no data), what helped me solve this was this addendum.

I still needed the initial '' to be a valid input type, so instead of returning an Invalid Date (new Date('')) in the else case, I just needed to return undefined.

Final fix:

    dob: yup.date().when([], (schema: yup.DateSchema) => {
      return boolCheck1 && boolCheck2
        ? yup.string()
        : schema
            .min(
              moment.utc().add(1, 'day').subtract(126, 'years').toDate(),
              'an error message'
            )
            .max(
              moment.utc().subtract(15, 'years').toDate(),
              'another error message'
            )
            .transform((_, originalValue) => {
              const date: moment.Moment = moment.utc(
                originalValue,
                'MM/DD/YYYY'
              );
              return date.isValid() ? date.startOf('day').toDate() : undefined;
            })
            .typeError('A valid date is required (MM/DD/YYYY)')
            .required('Please enter a valid date');
   })

(To be fair... I tried testing on the UI and there was no instance where this .typeError was ever triggered (even with Formik) since every allowable input instance (only numbers can be typed and they're auto-formatted to dates) was already being checked by the other tests... I think I could safely remove that typeError check altogether lol.)

Related