How to use yup validation on dynamic form using react-hook-form with useFieldArray
Asked Answered
G

6

8

I'm trying to create a dynamic form using react-hook-form's useFieldArray hook. The user should be able to add or remove fields, thus making it dynamic. I've looked at this tutorial for inspiration, but the missing piece is how to implement error validation to the state, which will be an array of objects: {email: string}[]. (The object will take more key/value pairs. I've left out the rest for simplicity.)

I've tried using yup as validation schema. It looks like this:

const schema = yup.array().of(
  yup.object().shape({
    email: yup.string().email().required(),
  }),
)

The implementation into react-hook-form is:

import * as yup from 'yup'
import { yupResolver } from '@hookform/resolvers/yup'
import { useForm, useFieldArray, Controller } from 'react-hook-form'

const { register, control, handleSubmit, errors } = useForm({
  resolver: yupResolver(schema),
  mode: 'onChange',
})
const { fields, append, remove } = useFieldArray({
  control,
  name: 'users',
})

The form is more or less according to the tutorial in the link above.

When console logging the error object from useForm hook it is consistently giving an empty object {}. It doesn't seem like it works. I am probably missing something here. The question is what?

Goodspeed answered 18/2, 2021 at 19:32 Comment(0)
E
9

React Hook Form v7

yup with dynamic fields react hook form - useFieldArray

import { string, object, array } from 'yup';

const formSchema = {
  name: string().required("Name is required").min(7, 'Message'),
};

const schema = object({
  test: array()
   .of(object().shape(formSchema))
});

export default schema;
const methods = useForm({
  resolver: yupResolver(schema),
});

show the error

<div style={{ fontSize: 14 }}>{errors?.test?.[index]?.name?.message}</div>
Epistemic answered 2/5, 2022 at 6:30 Comment(1)
Thanks for the code sample, Felipe. I got my dynamic fields validating now 👍.Samons
P
4

perhaps you want to use the context argument to switch your schema?

Context: This context object is mutable and will be injected into resolver's second argument or Yup validation's context object.

import * as React from "react";
import { useForm } from "react-hook-form";
import * as Joi from "joi";

const validationSchema1 = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required()
});

const validationSchema2 = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required()
});

const App = () => {
  const [schemaContext, setSchemaContext] = useState({ schemaA: false })
  const { register, handleSubmit, errors } = useForm({
    context: schemaContext, // use the context switch here
    resolver: async (data, context) => {
      const { error, value: values } = context.is1 ? validationSchema1.validate(data, {
        abortEarly: false
      }) : validationSchema2.validate(data, {
        abortEarly: false
      });

      return {
        values: error ? {} : values,
        errors: error
          ? error.details.reduce((previous, currentError) => {
              return {
                ...previous,
                [currentError.path[0]]: currentError
              };
            }, {})
          : {}
      };
    }
  });

  const onSubmit = data => {
    console.log(data)
  };

  return (
    <div className="App">
      <h1>resolver</h1>
      
      <form onSubmit={handleSubmit(onSubmit)}>
        <label>Username</label>
        <input type="text" name="username" ref={register} />
        {errors.username && <p>errors.username.message</p>}
        <input type="submit" />
      </form>
    </div>
  );
};
Prophets answered 22/2, 2021 at 2:16 Comment(0)
R
2

I used the useForm hook's resolver property method to identify the validation scheme I should use from the value of a field in my form.

The code below worked for my use case maybe it can help you

function formValidatorSchemaByPaymentModalityType(paymentModalityType?: ModalityTypes) {
    switch (paymentModalityType) {
        case ModalityTypes.CREDIT_CARD:
          return creditCardSchemaValidation;
        default:
        return defaultSchemaValidation;
    }
}

const methods = useForm({
    resolver: (data, context, options) => {
      const validatorSchema = formValidatorSchemaByPaymentModalityType(
        data.paymentType.value,
      )
      return yupResolver(validatorSchema)(data, context, options);
    },
});
Rostellum answered 26/5, 2022 at 22:16 Comment(0)
H
2

I had the same problem. I solved it by changing the controller name by referring to the register rules documentation!

Before:

`test[${index}].email`  // incorrect

After:

`test.${index}.email`  // correct
Holocene answered 1/2, 2023 at 9:34 Comment(0)
S
1

I think you should specify your array name. like this:

{
  teammates: yupArray().of(
      yupObject().shape({
        email: stringSchemaBuilder("Email").email(),
        expertise: arraySchemaBuilder(false, "Expertise", true, 2, 5),
      })
  ),
};

where teammates is my array name
Stockwell answered 6/3, 2021 at 12:50 Comment(0)
C
0
const { register, control, handleSubmit, formState: { errors } } = useForm({. resolver: yupResolver(schema),  mode: 'onChange' })

errors[`users[${index}].email`]
Checky answered 25/1, 2023 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.