1
votes

I'm using React Hook Form library. https://react-hook-form.com

If it's a simple form like Student Name, and Student Age, the application is quite simple.

const { register, handleSubmit, formState: { errors } } = useForm();

I just create those two input fields and register them.

               <div className="w-full flex flex-col mt-4">
                  <label>Name</label>
                  <input
                    type="text"
                    placeholder="Enter Name" {...register("name")}
                  />

                </div>
                <div className="w-full flex flex-col mt-4">
                  <label>Age</label>
                  <input
                    type="text"
                    placeholder="Enter Age" {...register("age")}
                  />

                </div>
                <div>
                  <button onClick={handleSubmit(submitData)}>
                    Update
                  </button>
                </div>

The submitData function will get the formData which can be used. The handleSubmit binds the registered fields to formData

const submitData = async formData => {
  console.log(formData)
}

The formData will look as below:

{"name":"test", "age":"27"}

My requirement is to make this form dynamic, by allowing me to add many students. I should be able to repeat a set of these fields with a button called "Add Student". Every time I add a new student, it should create a new row of these two fields where I can add new students names. finally, the output of the formData should look like an array of students:

[{"name":"test1", "age":27},{"name":"test2", "age":28},{"name":"test3", "age":29} ]

I can create the UI to add the new fields, but I don't know how to bind them to the formData in react hook form. In the documentation, I couldn't find how to do this with react hook form.

Please help.

2
Can you please provide your full code, including usage of the useForm hook? - Dmytro Krasnikov
There isn't much other than what I've provided above. The useForm usage is: const { register, handleSubmit, formState: { errors } } = useForm(); And from this register is used to register fields, which is shown above. - asanas
Here is a whole video on the process: youtu.be/61AlrA5BXzg, here is the sample from react-hook-form: codesandbox.io/s/react-hook-form-usefieldarray-ssugn - Aaron Saunders

2 Answers

1
votes

I tried the following:

I created a state as follows:

const [items, setItems] = useState([0,1,2])

I can update this when I add/remove items.

Now, for the fields, I wrapped them into a map as follows:

{items.map(i => (
    <div>
        <div className="w-full flex flex-col mt-4">
            <label>Name</label>
            <input
            type="text"
            placeholder="Enter Name" {...register(`items[${i}].name`)}
            />

        </div>
        <div className="w-full flex flex-col mt-4">
            <label>Age</label>
            <input
            type="text"
            placeholder="Enter Age" {...register(`items[${i}].age`)}
            />

        </div>
    </div>
))}

This works.

0
votes

Disclaimer

This isn't working as expected yet, but it should give you ideas that can get you there.

My approach to solving this was having a form component for each student, all the student forms have an id, and it's stored in an array that is mapped through and rendered.

I used the useFormContext to manage form state across multiple the multiple form components

import { useState } from 'react';
import { useForm, FormProvider, useFormContext } from 'react-hook-form';
import './styles.css';

const StudentForm = (props) => {
  const { register, handleSubmit } = useFormContext();

  return (
    <>
      <div className="w-full flex flex-col mt-4">
        <label>Name</label>
        <input
          type="text"
          placeholder="Enter Name"
          {...register(`name-${props.id}`)}
        />
      </div>
      <div className="w-full flex flex-col mt-4">
        <label>Age</label>
        <input
          type="text"
          placeholder="Enter Age"
          {...register(`age-${props.id}`)}
        />
      </div>
    </>
  );
};

const initialStudentForms = [
  {
    id: 'student-one',
    Form: StudentForm,
  },
];

export default function App() {
  const [studentForms, setStudentForms] = useState(initialStudentForms);
  const methods = useForm();

  const addStudent = () => {
    setStudentForms([
      ...studentForms,
      {
        id: 'new-generated-id',
        Form: StudentForm,
      },
    ]);
  };

  const submitData = async (formData) => {
    console.log(formData);
  };

  return (
    <FormProvider {...methods}>
      <div className="App">
        {studentForms.map(({ id, Form }) => (
          <Form key={id} id={id} />
        ))}
        <br />
        <button onClick={methods.handleSubmit(submitData)}>Update</button>{' '}
        <button onClick={addStudent}>Add Student</button>
      </div>
    </FormProvider>
  );
}

For further tinkering, here's the link on sandbox https://codesandbox.io/s/elastic-bohr-qp524?file=/src/App.js