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
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;
}