Transform Yup validation error into a useable object
Asked Answered
L

3

8

Problem

I have a formik form which needs to have 2 different validation schemas depending on what button the user uses to submit. I have seen some people say use state to decide which one but I want to avoid using state as it feels wrong in this case.

I have viewed Yup's documentation and it seems you can just validate using a schema directly and passing the values. This seems to work as I have shown in my example however the validation errors it returns are useless and I need to transform them to be able to use the Formik setErrors helper.

Yup validation as per documentation

let validationErrors = null;

try {
  // Validate the form data using a custom Schema
  await createDraftContractorFormValidationSchema.validate(values, { abortEarly: false, strict: false });
}
catch (errors: any) {
  console.log(errors);
  // What I essentially need here is a way to transform these errors
  // into an object with the keys being the field that has errors and the message
  if (errors) {
     formikRef.current.setErrors(errors);
  }
}

What gets logged

ValidationError: 4 errors occurred
    at finishTestRun (runTests.js:54:1)
    at runTests.js:8:1
    at finishTestRun (runTests.js:54:1)
    at runTests.js:8:1
    at finishTestRun (runTests.js:54:1)
    at createValidation.js:60:1
Lukasz answered 20/8, 2022 at 8:20 Comment(0)
L
11

What I ended up doing I found in some obscure forum but thought it might be useful on Stack for others in the future. Credit for answer https://lightrun.com/answers/jaredpalmer-formik-yup-schemavalidate-options-show-every-error-of-a-field-at-the-same-time.

Essentially I created a helper method that transformed Yup's validation error into a useful object which you can pass directly into Formik's setErrors method.

Helper

/**
 * TransformYupErrorsIntoObject
 *
 * @description Transform the useless yup error into a useable validation object
 * @param {ValidationError} errors Yup validation errors
 * @returns {Record<string, string>} Validation errors
 */
export const transformYupErrorsIntoObject = (errors: ValidationError): Record<string, string> => {
  const validationErrors: Record<string, string> = {};

  errors.inner.forEach((error: any) => {
    if (error.path !== undefined) {
      validationErrors[error.path] = error.errors[0];
    }
  });

  return validationErrors;
};

Implementation

try {
  // You need to define this schema as a model of your form
  // Abort early will ensure all the fields validate not just one
  await createDraftContractorFormValidationSchema.validate(values, { abortEarly: false, strict: false });
}
catch (error: any) {
  // Transform the validationErrors
  const validationErrors = transformYupErrorsIntoObject(error);

  // Set errors on the field showing the user the errors
  formikRef.current.setErrors(validationErrors);

  return;
}
Lukasz answered 20/8, 2022 at 8:20 Comment(1)
Exactly what I needed as an example, thank you so much!Intractable
I
0

A JavaScript version of transforming Yup errors (array of strings) to JavaScript Object (key: value pairs). Based on Vuk's contributions--@Vuk

/**
 * TransformYupErrorsIntoObject
 *
 * @description Transform the useless yup error into a useable validation object
 * @param {ValidationError} errors Yup validation errors
 * @returns {Record<string, string>} Validation errors
 */



 export const YupErrorsIntoObject = (errors) => {
      const validationErrors = {};
      errors.inner.forEach((error) => {
      if (error.path !== undefined) {
      validationErrors[error.path] = error.errors[0];
    }
  });

  return validationErrors;

};

Illfated answered 5/9 at 16:22 Comment(0)
P
-1
const [practiceData, setPracticeData] = useState({
    firstName: '',
    lastName: '',
    role: '',
    email: '',
    phoneNumber: '',
  })
  const [Error, setError] = useState({ isErr: false, message: '' })

const handleSubmit = (e) => {
    e.preventDefault()
    schema
      .validate(practiceData, { abortEarly: false })
      .then((val) => console.log(val))
     //I've got all the errors just by writing err.errors in message.
      .catch((err) => setError({ isErr: true, message: err.errors }))

  }

//Output
 ['First Name is required!', 'Last Name is required!', 'Role is required!', 'Email is required!', 'phoneNumber must be a `number` type, but the final value was: `NaN` (cast from the value `""`).']
Polyclinic answered 20/10, 2022 at 8:29 Comment(1)
Hello, please see meta.stackoverflow.com/editing-help Thanks!Permeability

© 2022 - 2024 — McMap. All rights reserved.