DEV Community

Fredrick Mgbeoma
Fredrick Mgbeoma

Posted on • Originally published at codeisbae.com on

Dynamic Form Fields in React

Dynamic Forms Thumbnail

If you’ve been building applications that accept user input via forms, you might have come across the need to make some form fields dynamic. This could be for various reasons, for instance, to add multiple members as part of a team during creation. If you’ve never been in this situation, this post might also be helpful for you at some point.

Prerequisites:

  • Working knowledge of React Hooks
  • npm installed in machine

We’ll be illustrating dynamic form fields in React using a simple form that has two input fields. One for the first name and another for the last name, these would form the user data. These two fields would be dynamically duplicated to create more fields that are unique and can accept new user data.

Getting Started

To quickly get started, we would be using the Create React App package, but the steps we would go over can be seamlessly integrated into your existing application.

Follow the steps below to create a new React app, navigate into the app directory and start it up in development mode.

npx create-react-app my-app
cd my-app
npm start

Open http://localhost:3000 to view it in the browser.

For some styling, we would install Bootstrap. This step is optional and only included here to give the form some good user interface. Knowledge of Bootstrap is not required.

npm i bootstrap

Deep Dive

Open the project in your favourite text editor. Navigate to App.js and replace the content with the code snippet below:

import React, { useState, Fragment } from "react";

import "bootstrap/dist/css/bootstrap.css";

const App = () => {

  return (
    <>
      <h1>Dynamic Form Fields in React</h1>
    </>
  )
}

export default App;

This gives us a basic template on which to build our form component.

Using React Hooks, initialise the form input fields like so:

...

const App = () => {
  const [inputFields, setInputFields] = useState([
    { firstName: '', lastName: '' }
  ]);

...

}

...

In the above snippet, inputFields refers to the form fields, while setInputFields is the used to set the value for these form fields

Proceed to setup the form by updating the App component as shown below:

const App = () => {
...

const handleSubmit = e => {
    e.preventDefault();
    console.log("inputFields", inputFields);
  };

return (
    <>
      <h1>Dynamic Form Fields in React</h1>
      <form onSubmit={handleSubmit}>
        <div className="form-row">
          {inputFields.map((inputField, index) => (
            <Fragment key={`${inputField}~${index}`}>
              <div className="form-group col-sm-6">
                <label htmlFor="firstName">First Name</label>
                <input
                  type="text"
                  className="form-control"
                  id="firstName"
                  name="firstName"
                  value={inputField.firstName}
                />
              </div>
              <div className="form-group col-sm-4">
                <label htmlFor="lastName">Last Name</label>
                <input
                  type="text"
                  className="form-control"
                  id="lastName"
                  name="lastName"
                  value={inputField.lastName}
                />
              </div>
              <div className="form-group col-sm-2">
                <button
                  className="btn btn-link"
                  type="button"
                >
                  -
                </button>
                <button
                  className="btn btn-link"
                  type="button"
                >
                  +
                </button>
              </div>
            </Fragment>
          ))}
        </div>
        <div className="submit-button">
          <button
            className="btn btn-primary mr-2"
            type="submit"
            onSubmit={handleSubmit}
          >
            Save
          </button>
        </div>
      </form>
    </>
  )

}

...

If that looks like a lot of code thrown right at you, don’t worry, we’ll go over it. We have the function handleSubmit to log the value returned when the form is submitted. Here we are simply logging to the console, but you could use the data here for whatever you need depending on your specific use case. The JavaScript map function is used to create an array of the form fields. Fragment from React allows us to group a list of children without adding a new node to the DOM.

Adding Functionality to the Form

Handling Changes

At this point, the basic UI is done that means we are getting closer to our goal, but there is no functionality yet. Roll up your sleeves, let’s get to work on the functionality!

Update the input fields to include a change handler to cater for user input action. See the change below:

...

  <input
...
   onChange={event => handleInputChange(index, event)}
  />

...

The handleInputChange function does not exist. Let’s create it in the App component right after handleSubmit. See below:

...
const handleInputChange = (index, event) => {
    const values = [...inputFields];
    if (event.target.name === "firstName") {
      values[index].firstName = event.target.value;
    } else {
      values[index].lastName = event.target.value;
    }

    setInputFields(values);
  };
...

In the above code snippet, we spread inputFields and do a simple check for the input field based on the name attribute of that field. Then we supply the value for the given index. The index is derived from the map function we used previously.

Adding and Removing Form Fields

Now comes the interesting part. Right after the handleInputChange, add the following code snippet to handling adding and removing form fields:

...

  const handleAddFields = () => {
    const values = [...inputFields];
    values.push({ firstName: '', lastName: '' });
    setInputFields(values);
  };

  const handleRemoveFields = index => {
    const values = [...inputFields];
    values.splice(index, 1);
    setInputFields(values);
  };

...

To make use of these two new functions, update the - and + buttons to include click handlers to add and remove form fields.

...
              <div className="form-group col-sm-2">
                <button
                  className="btn btn-link"
                  type="button"
                  onClick={() => handleRemoveFields(index)}
                >
                  -
                </button>
                <button
                  className="btn btn-link"
                  type="button"
                  onClick={() => handleAddFields()}
                >
                  +
                </button>
              </div>
...

Aside: To see a preview of the form input values, add the following code snippet before the closing form tag </form>:

...
<br/>
<pre>
 {JSON.stringify(inputFields, null, 2)}
</pre>
...

There you go! To add more form fields, click on +, to remove click on -. You can implement this functionality in your existing applications or new ones.

Preview:

Free free to share this with your team!

Originally published at: https://codeisbae.com/dynamic-form-fields-in-react/

Latest comments (10)

Collapse
 
kesav7902 profile image
kesav7902

its a great article, how can i read the repeater control values in the parent class. Can you pls help with a sample ? thanks in advance.

Collapse
 
mathewsnyirongo profile image
Mathews Chiza Nyirongo

Great discussion. Thumbs up for thoroughly explaining everything while keeping an eye on the goal. Simple and straight to the point.

Collapse
 
dendekky profile image
Ibrahim Adeniyi

Nice article! Helped when I needed it

Collapse
 
ryandowd profile image
ryandowd • Edited

Nice article! It's helped me with the React side of a MERN application I'm trying to write.

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
fuchodeveloper profile image
Fredrick Mgbeoma

Hey Duane, sorry for the late reply.
I'm not sure if you have resolved this yet. But, if not, maybe you could share some code snippet to give more context on the issue?

Collapse
 
foxbit19 profile image
foxbit19

Thanks for sharing!
I like this trick to mantain the original values untouched: const values = [...inputFields];.
It's better to create a copy...even more when the original values are used as component state.

Collapse
 
fuchodeveloper profile image
Fredrick Mgbeoma

I'm glad to know the article was helpful to you.
Thanks for your feedback.

Collapse
 
benfluleck profile image
Benny Ogidan

I like this article however I think you could use the useReducer hook especially as the form starts to grow with the addition of error messages.

Collapse
 
fuchodeveloper profile image
Fredrick Mgbeoma

Thanks, Benny. That sounds like an approach to give a try!