简体   繁体   中英

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

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?

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>

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

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

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
const { register, control, handleSubmit, formState: { errors } } = useForm({   resolver: yupResolver(schema),   mode: 'onChange', })

errors[`users[${index}].email`]

I had the same problem. I solved it by changing the controller name by referring to the following!

`test[${inde}].email` ->  `test.${index}.email` 

Reference Links: https://legacy.react-hook-form.com/api/useform/register -> Rules

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM