25

I have a form with multiple components in it (with each being either a functional or a class based component) containing multiple input fields or radio buttons. When I submit the form I either want to submit the fields that are nested into components along with the form data or I should be able to extract the fields data and then submit them (not sure which approach would be the best and why?). How can I achieve this?

Code :

import React from "react";
import { useForm } from "react-hook-form";

export default function TestComponent() {
  const { register, handleSubmit, errors } = useForm();
  const onSubmit = data => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">Name</label>
      <input type="text" id="name" name="name" ref={register({ required: true, maxLength: 30 })} />
      {errors.name && errors.name.type === "required" && <span>This is required</span>}
      {errors.name && errors.name.type === "maxLength" && <span>Max length exceeded</span> }
      <NestedComponent1 />
      <NestedComponent2 />
      <input type="submit" />
    </form>
  );
}

function NestedComponent1() {
    return (
        <div>
            <input type="text" id="nested-name" name="nestedName" />
            <input type="text" id="nested-name2" name="nestedName2" />
            <input type="text" id="nested-name3" name="nestedName3" />
        </div>
    );
}

function NestedComponent2() {
    return (
        <div>
            <input type="text" id="nested-comp2-name" name="nestedcomp2Name" />
            <input type="text" id="nested-comp2-name2" name="nestedcomp2Name2" />
            <input type="text" id="nested-comp2-name3 name="nestedcomp2Name3" />
        </div>
    );
}
Ahmed
  • 2,966
  • 7
  • 42
  • 69

2 Answers2

22

You could use the hook useFormContext to avoid to pass the context as a prop https://react-hook-form.com/api/useformcontext

You only need to wrap your form with the FormProvider component so that you can get context using useFormContext in your nested component

export default function TestComponent() {
    const methods = useForm();
    const { register, handleSubmit, errors } = methods;
    const onSubmit = data => console.log(data);

    return (
    <FormProvider {...methods} > // pass all methods into the context
        <form onSubmit={handleSubmit(onSubmit)}>
        <label htmlFor="name">Name</label>
        <input type="text" id="name" name="name" ref={register({ required: true, maxLength: 30 })} />
        {errors.name && errors.name.type === "required" && <span>This is required</span>}
        {errors.name && errors.name.type === "maxLength" && <span>Max length exceeded</span> }
        <NestedComponent1 />
        <input type="submit" />
        </form>
    </FormProvider>
    );
}

function NestedComponent1() {
    const { register } = useFormContext(); // retrieve all hook methods
    
    return (
        <div>
            <input {...register("nestedName")} type="text" id="nested-name" name="nestedName"  />
            <input {...register("nestedName2")} type="text" id="nested-name2" name="nestedName2" />
            <input {...register("nestedName3")} type="text" id="nested-name3" name="nestedName3" />
        </div>
    );
}

Larissa Bentes
  • 221
  • 2
  • 4
  • 1
    Hello @Larissa, thks for your answer. It's works. I am using your approach with Yup validation schema on the main component. When one field of the nested form is not valid the error message of Yup schema is not being replicated to the nested component. Did you know how to achieve this? – Cassio Seffrin Oct 25 '22 at 12:53
  • thanks this is also worked with me – Asmaa3107 Jan 04 '23 at 13:38
  • you cand sending errors as props to your nested component it works with me – Asmaa3107 Jan 04 '23 at 16:30
4

To extract your data from the nested components you can add "useState" in your TestComponent and pass down an onChange function to the nested components.

import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit, errors } = useForm();
  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">A</label>
      <input
        type="text"
        id="name"
        name="name"
        ref={register({ required: true, maxLength: 30 })}
      />
      {errors.name && errors.name.type === "required" && (
        <span>This is required</span>
      )}
      {errors.name && errors.name.type === "maxLength" && (
        <span>Max length exceeded</span>
      )}
      <NestedComponent1 register={register} />
      <NestedComponent2 register={register} />
      <input type="submit" />
    </form>
  );
}

function NestedComponent1({register}) {
  return (
    <div style={{ display: "flex", flexDirection: "column" }}>
      <label htmlFor="nestedName">B</label>
      <input type="text" id="nested-name" name="nestedName" ref={register} />
      <label htmlFor="nesteNamename2">C</label>
      <input type="text" id="nested-name2" name="nestedName2" ref={register} />
      <label htmlFor="nestedName3">D</label>
      <input type="text" id="nested-name3" name="nestedName3" ref={register} />
    </div>
  );
}

function NestedComponent2({ register }) {
  return (
    <div style={{ display: "flex", flexDirection: "column" }}>
      <label htmlFor="nestedcomp2Name">E</label>
      <input
        type="text"
        id="nested-comp2-name"
        name="nestedcomp2Name"
        ref={register}
      />
      <label htmlFor="nestedcomp2Name2">F</label>
      <input
        type="text"
        id="nested-comp2-name2"
        name="nestedcomp2Name2"
        ref={register}
      />
      <label htmlFor="nestedcomp2Name3">G</label>
      <input
        type="text"
        id="nested-comp2-name3"
        name="nestedcomp2Name3"
        ref={register}
      />
    </div>
  );
}
Yardie
  • 53
  • 5
  • Cant we do the same through `React-hook-form`? I mean isnt there a way for us to just use that to collect all the nested fields data? cause when I hit submit I want the fields data to be read by React Hook Form – Ahmed Dec 10 '20 at 22:49
  • 2
    I changed my answer, check above. All you need to do is send down the register method down to the children. Also check the official docs of react-hook-form: https://react-hook-form.com/advanced-usage/ – Yardie Dec 12 '20 at 12:24
  • https://react-hook-form.com/advanced-usage/ this might be helpful – nbpeth Dec 30 '20 at 18:54
  • 1
    This was the only solution that worked well for me. – leyla azari Dec 01 '21 at 05:54