Skip to content
Advertisement

(Yup) how to create multiple errors using a single .test() function

EDIT: while the accepted solution worked, this worked much better in my use case

I have one function that validates that neither input field A nor input field B are empty and with the way my form is built I have to write only one function to check both. (The actual function is much more complicated so I opted to create the example function below)

this is my test function:

function isValid(message) {
    //I don't use the message variable but I added it anyway
    return this.test("isValid", message, function (value) {
    if(!value.A) {
        return createError({path: `${this.path}.A`, message:"A is empty"});
    }
    if(!value.B) {
        return createError({path: `${this.path}.B`, message:"B is empty"});
    }
    return true;
    })

The result of this is that when A and B are empty I return the first createError so the rest of the function is skipped and this is what the formik.errors object looks like:

{
    parent: {
        A: "A is empty"
    }
}

How do I create an array of errors and return it instead?

I tried:

returning an array of createErrors() but I got the same result,

using createErrors with an array of paths and messages but the formik.errors looked like this:

{
    parent.A: { parent.B: "2 errors occured" }
}

instead of the desired:

{
    parent: {
        a: "A is empty",
        b: "B is empty"
    }
}

Advertisement

Answer

You can use ValidationError from Yup to achieve this goal. The constructor of ValidationError accepts array of other ValidationError instances. The individual ValidationError instances are capable of holding path and error message and the parent instance will combine them.

Here’s the example test function:

import { ValidationError } from 'yup'

const testFunc = (value) => {
    const keys = ['A', 'B']

    const errors = keys.map((key) => {
        if (value[key]) { return null } // Successful validation, no error

        return new ValidationError(
          `${key} is empty`,
          value[key],
          key
        )
    }).filter(Boolean)

    if (errors.length === 0) { return true }

    return new ValidationError(errors)
}

It is also possible to do the same thing with createError judging by the source code.

You can pass a function instead of a string to message property of createError call. In this case the function would be called within createError => ValidationError.formatError here. And if this function returned an array of the properly built VaildationError instances we would get the same result.

Here’s an example test function for that approach:

import { ValidationError } from 'yup'

const testFunc = (value, { createError }) => {
    const keys = ['A', 'B']

    const errors = keys.map((key) => {
        if (value[key]) { return null } // Successful validation, no error

        return new ValidationError(
          `${key} is empty`,
          value[key],
          key
        )
    }).filter(Boolean)

    if (errors.length === 0) { return true }

    return createError({
      message: () => errors
    })
}
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement