Is there a way to set defaultValues in a child component using react hook form with useFieldArray hook - not using the useForm hook?

Viewed 639

Problem

I want to implement key/value pairs of input fields in a form that can be added by a user.

In addition, I want to display saved data when the user has submitted the form and the page is displayed again.

Preconditions

  • I'm using react-hook-form V7 (RHF) and its useFieldArray hook.
  • As I use Material-UI I have to use controlled components.

Working solution

In a simplified app, I have a parent component using the useForm hook and two child components, one for the purpose of demonstration holding normal form fields and an <ArrayFields /> component holding the array fields.

Yesterday I learned by this answer that one way to do it is to set the defaultValues object in the parent's useForm hook like this:

const methods = useForm({
    defaultValues: {
      email: "john.smith@example.com",
      firstName: "John",
      lastName: "Smith",
      systemRole: "Admin",
      envRoles: [                             // <-- saved dynamic fields
        { envName: "foo1", envRole: "bar1" },
        { envName: "foo2", envRole: "bar2" }
      ]
    }
  });

Here you can see a codesandbox of this working solution.

Question

Nonetheless, I am wondering if it isn't possible to set the defaultValues in the <ArrayFields /> child component?

useFormContext approach

For example like this using the useFormContext hook:

// 
const ArrayFields = ({ fieldset }) => {
  const { control, getValues } = useFormContext({
    defaultValues: {
      envRoles: [
        { envName: "foo1", envRole: "bar1" },
        { envName: "foo2", envRole: "bar2" }
      ]
    }
  });
  const { fields, append, remove } = useFieldArray({
    control,
    name: "envRoles"
  });

  ...
}

Props approach

Next, I tried to pass the fields (alias envRoles) as props and set the defaultValues to the Controller directly

// index.js:30
<ArrayFields
  fieldset={[
    { envName: "foo1", envRole: "bar1" },
    { envName: "foo2", envRole: "bar2" }
  ]}
/>

// ArrayFields.js:35
<Controller
  render={({ field }) => <input {...field} />}
  defaultValue={item.envName}                   {/* <-- defaultValue on Controller */}
  name={`envRoles[${index}].envName`}
  control={control}
/>

Question again

So is it really true that RHF does not allow handling all things that matter to the component inside this component?

Thanks in advance for your hints.

1 Answers

I think I found the answer.

In the documentation of useFieldArray you'll find the replace method.

replace (obj: object[]) => void     Replace the entire field array values.

So using a useEffect hook it's finally easy.

useEffect(() => {
  replace(fieldset);
}, [fieldset, replace]);

In this codesandbox you will find the example that finally works with setting the defaultValues in the child component.

Related