yup schema .compact() method manipulates form data submitted

Viewed 18

I've got a form using react-hook-form that validates submission by checking against a schema defined in yup.

It works well, but if I apply .compact() to remove false values in order to count how many true values in the array with .min(1), the data that comes out the other end of the handleSubmit(onSubmit) function has been manipulated by the schema validation process, and false values no longer reach the onSubmit function.

const schema = yup
  .object({
    roles: yup
      .array()
      .compact()
      .min(1, "A user must have at least one role assigned"),
  })
  .required();

const MyForm = () => {
  const { handleSubmit } = useForm({resolver: yupResolver(schema)});

  const roles = ["foo", "bar", "baz"];

  const onSubmit = ({roles}) => {
    console.log(roles); // [true, true]
    const selectedRoles = roles.filter((_r, i) => roleData[i]);
    console.log(selectedRoles); // ["foo", "bar"]; <— incorrect roles due to loss of false values
  }

  <form onSubmit={handleSubmit(onSubmit)}>
    // ...form
    // example submission data from my form: `{ roles: [true, false, true] }`
    // ✅ foo
    // ❌ bar
    // ✅ baz
  </form>
};
  1. Is there a way to stop yup from manipulating the output of the form, and only being used for schema validation?
  2. If not, is there a way to return the full values after .compact() has removed some?
  3. Is there a different way to validate that an array has at least x true values that won't manipulate the output of the form submission data?

Any help much appreciated.

1 Answers

I don't know if there's a way to stop yup from manipulating the output of the data submitted at the end, but I've found a way to do the equivalent of .compact().min(1) using the .test(name: string, message: string | function | any, test: function) method:

const schema = yup
  .object({
    roles: yup
      .array()
      .test(
        "min-1-true",
        "A user must have at least one role assigned",
        value => {
          const trueValues = value?.filter(v => v === true).length;
          return trueValues >= 1;
        },
      ),
  })
  .required();
Related