Don’t use State for React Forms. Use this instead!
Tags: react, reactforms, javascript, formdata
Published: 2023-08-31 15:18:03

---
title: Don’t use State for React Forms. Use this instead!
published: true
date: 2023-08-31 04:16:26 UTC
tags: react,reactforms,javascript,formdata
canonical_url: https://javascript.plainenglish.io/dont-use-state-for-react-forms-use-this-instead-7e2923550cd1
---

Learn how to optimize the performance of react forms by replacing useState with the native feature of JavaScript.

![Cover Image](https://cdn-images-1.medium.com/max/1024/0*mxFCMzxkEIx36nhK)
_Photo by Lautaro Andreani on Unsplash_

## Introduction

When it comes to handling forms in react, the most popular approach is to store the input values in state variables. One of the reasons for following this approach is because, it's React, after all, and everyone tends to use the hooks that come with it. Using hooks solves a lot of problems in React, but is it required when it comes to forms? Let's check it out.

## Problem with using States

As we already know, whenever the value of the state variable changes inside a component, react will re-render the component to match its current state. Though it's not a big issue in small applications, it may cause performance bottlenecks as your application grows in size.  When it comes to form, React will attempt to re-render the component every time the input (state) changes.

---

Side Tip: I came across this [answer](https://stackoverflow.com/questions/72773150/how-i-can-count-how-many-times-my-component-rendered-in-a-react-component#answer-72773234) on StackOverflow which is very useful for counting the number of times a component has been rendered. We will use that utility function in our code as well.

---

Let's implement and see the issue with states in action.

Create a basic react app using Vite and clean up unwanted files once the project is created.

```shell
npm create vite@latest my-vue-app -- --template react

# yarn
yarn create vite my-vue-app --template react

# pnpm
pnpm create vite my-vue-app --template react
```

Let's create a react component (say `FormWithState`) containing a form that takes in two inputs email and password. We will use the state to manage the form inputs.

```jsx
import { useEffect, useRef, useState } from "react";
import "./Forms.css";

export default function FormWithState() {
  // The count will increment by 2 on initial render due to strict mode then by 1 on subsequent renders
  const countRef = useRef(0);
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  useEffect(() => {
    countRef.current = countRef.current + 1;
  });

  function handleSubmit(e) {
    e.preventDefault();
    console.log({ email, password });
  }

  return (
    <div className="form-div">
      <h2>Form With State</h2>
      <form onSubmit={handleSubmit}>
        <div className="input-field">
          <label htmlFor="email2">Email</label>
          <input
            id="email2"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            autoComplete="off"
          />
        </div>
        <div className="input-field">
          <label htmlFor="password2">Password</label>
          <input
            id="password2"
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
        </div>
        <button type="submit">Submit</button>
        <div>
          <p>
            The Component Re-Rendered <span>{countRef.current}</span> times
          </p>
        </div>
      </form>
    </div>
  );
}
```

Add this component to the `App` component and open `http://localhost:5173`

![User entering the email and password. The component render count is updated as and when the user types in.](https://cdn-images-1.medium.com/max/470/1*Bcf8_2zx1k-TeFDf59QMCw.gif)

As you can see, the form component is rendered about 23 times and the count will increase gradually as the number of input fields increases. In most cases, the form values are used only during the form submission. So, is it required to re-render the component about 20+ times just for two input fields? The answer is a clear **NO!**

Also, when the number of input fields increases, the number of state variables to store the input values increases, thereby increasing the complexity of the codebase. So, what's the alternative approach to avoid re-renders but achieving all the functionalities of the forms?

## Using FormData to handle forms

So, the alternative approach is to use the native [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) interface of JavaScript.

There are three ways to create a new `FormData` object as described in the [official docs](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData).

```javascript
new FormData();
new FormData(form);
new FormData(form, submitter);
```

We will be using the second method because we already have a form. We just need to pass the form element to the constructor and it will auto-populate the form values. To make this work, we also need to add the `name` attribute to the `input` tag. Let's test this approach. Create a component (say `FormWithoutState`).

```jsx
import { useEffect, useRef } from "react";

export default function FormWithoutState() {
  // The count will increment by 2 on initial render due to strict mode then by 1 on subsequent renders
  const countRef = useRef(0);

  useEffect(() => {
    countRef.current = countRef.current + 1;
  });

  function handleSubmit(e) {
    e.preventDefault();
    const form = new FormData(e.currentTarget);
    const email = form.get("email");
    const password = form.get("password");
    console.log({ email, password });
    const body = {};
    for (const [key, value] of form.entries()) {
      body[key] = value;
    }
    console.log(body);
    // Do Further input validation and submit the form
  }

  return (
    <div className="form-div">
      <h2>Form Without State</h2>
      <form onSubmit={handleSubmit}>
        <div className="input-field">
          <label htmlFor="email1">Email</label>
          <input id="email1" type="email" name="email" autoComplete="off" />
        </div>
        <div className="input-field">
          <label htmlFor="password1">Password</label>
          <input id="password1" type="password" name="password" />
        </div>
        <button type="submit">Submit</button>
        <div>
          <p>
            The Component Re-Rendered <span>{countRef.current}</span> times
          </p>
        </div>
      </form>
    </div>
  );
}
```

In this component, we haven't used `useState` hook at all. Instead, we are adding the `name` attribute to the `input` tag. Once the user submits the form, in the `handleSubmit` function, we are creating the `FormData` by providing the form object via `e.currentTarget`. Then we iterate through the `FormData.entries()` method to get the form key and value to build the form body. We can then use this object for further input validation and submission via `fetch` or `Axios` API. But, what about the impact of component re-rendering of this approach? Let's check it out. Add this component to the `App` component and open `http://localhost:5173`.

![User entering the email and password. The component render count is updated as and when the user types in.](https://cdn-images-1.medium.com/max/445/1*mn7YDtruk_gNJD0hluWlDA.gif)

Aren't you surprised? The component didn't re-render at all.

## Advantages of using FormData

1. The form **input values are automatically captured** without the need to maintain a state variable for each input field.
2. The component **doesn't re-render** on user input.
3. The API request body can be easily built when using `FormData`, whereas we would need to assemble the data for submission when using `useState`.
4. It **eliminates** the need for introducing **new state variables** as and when the form grows.
5. When dealing with multiple forms, you might end up duplicating similar state variables across components, whereas `FormData` can be reused easily with just a few lines of code.
6. One thing that `FormData` supports out of the box is, it will handle dynamic fields automatically. i.e., If your form has **dynamically generated fields** (adding/removing fields based on user input), managing their state with `useState` requires additional handling, whereas `FormData` will take care of it automatically.

## Conclusion

You can check the code for this article on code sandbox [here](https://codesandbox.io/s/elated-sun-flx2nr). Hope you learned something new from this article. Leave a comment if you have any doubts. Thanks!