How to make forms reusable and dynamic in React.

We've all seen forms on different websites before so we know how big they can get with lots of different fields. Making these types of forms in react can be a bit tedious if you don't know how to make them more dynamic and reusable.

Up until now the typical way to make a form is to set State for each input field...first name, last name, address, what have you. If you were to create a basic form to collect someone's contact information, it might look something like the below example:

import React, { useState } from "react";

function Form() {
  const [firstName, setFirstName] = useState("Sarah");
  const [lastName, setLastName] = useState("Nosal");
  const [address, setAddress] = useState("123 Zephyr Cove")
  const [email, setEmail] = useState("[email protected]");


  function handleFirstNameChange(event) {
    setFirstName(event.target.value);
  }

  function handleLastNameChange(event) {
    setLastName(event.target.value);
  }

  function handleAddressChange(event) {
    setAddress(event.target.value);
  }

  function handleEmailChange(event) {
    setEmail(event.target.value);
  }

  return (
    <form>
      <input 
         type="text" 
         onChange={handleFirstNameChange} 
         value={firstName} />
      <input 
         type="text" 
         onChange={handleLastNameChange} 
         value={lastName} />
      <input 
         type="text" 
         onChange={handleAddressChange} 
         value={address} />
      <input 
         type="text" 
         onChange={handleEmailChange} 
         value={email} />
      <button type="submit">Submit</button>
    </form>
  );
}

export default Form;

As you can see, this can get pretty long and tedious, especially when you have multiple fields whose values you are trying to capture, which is not uncommon for a form on any website today. Plus, if you want to add anymore fields you would have to add a new state variable by calling useState() to hold the value of the new input field, as well as add a new handleChange function to handle updating state for that field.

Let's try making this form again using a dynamic onChange event handler. Our first step will be refactoring the state variables so we are only calling state once. To do that, we have to make an object representing all of our input fields as shown below:

import React, { useState } from "react";

function Form() {
  const [formData, setFormData] = useState({
     firstName: "Sarah",
     lastName: "Nosal",
     address: "123 Zephyr Cove",
     email: "[email protected]",
  })

This already looks way better than calling state 4 different times. If we need to add another input field in the future, rather than creating a new state variable and calling state for that new input field, we would simply add a new key:value pair to our formData object.

Next we have to update the handleChange functions to update the state (I will put all the code together at the end):

function handleChange(event) {
   const name= event.target.name;
   const value= event.target.value;

   setFormData({
      ...formData,
      [name]: event.target.value,
    });
 }

We are setting a new state whenever we submit the form, so we have to use the spread operator here to copy all the key/value pairs into our new state object. As you can see we have condensed all the handleChange functions for each input into one reusable function. The only way we can do this is if we give a name attribute to each input field and if that name attribute matches the key in formData object. So now if we wanted to add an input field to our form all we would have to do is add a key/value pair and add the name attribute to that new input that matches the corresponding key. Check out the final code below!!

import React, { useState } from "react";

function Form() {
  const [formData, setFormData] = useState({
    firstName: "Sarah",
    lastName: "Nosal",
    address: "123 Zephyr Cove",
    email: "[email protected]",
  });

  function handleChange(event) {
    const name = event.target.name;
    const value = event.target.value;

    setFormData({
      ...formData,
      [name]: value,
    });
  }

  function handleSubmit(event) {
    event.preventDefault();
    console.log(formData);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="firstName"
        onChange={handleChange}
        value={formData.firstName}
      />
      <input
        type="text"
        name="lastName"
        onChange={handleChange}
        value={formData.lastName}
      />
      <input
        type="text"
        name="address"
        onChange={handleChange}
        value={formData.address}
      />
      <input
        type="text"
        name="email"
        onChange={handleChange}
        value={formData.email}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

I hope you can apply this dynamic onChange event handler to any future forms you create to make them more streamlined and the code easier to read!!

22