Source:  Twitter logo

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

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

11 users liked answer #0dislike answer #011
Larissa Bentes profile pic
Larissa Bentes

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>
  );
}
3 users liked answer #1dislike answer #13
Yardie profile pic
Yardie

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).