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 , 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. React 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 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. answer 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. 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 ) containing a form that takes in two inputs: email and password. We will use the state to manage the form inputs. FormWithState 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 component and open App http://localhost:5173 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 interface of JavaScript. FormData There are three ways to create a new object, as described in the . FormData official docs 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 attribute to the tag. Let's test this approach. Create a component (say ). name input FormWithoutState 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 hook at all. Instead, we are adding the attribute to the tag. Once the user submits the form, in the function, we are creating the by providing the form object via . Then, we iterate through the 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 or API. But what about the impact of component re-rendering of this approach? Let's check it out. Add this component to the component and open . useState name input handleSubmit FormData e.currentTarget FormData.entries() fetch Axios App http://localhost:5173 Aren't you surprised? The component didn't re-render at all. Advantages of using FormData The form without the need to maintain a state variable for each input field. input values are automatically captured The component on user input. doesn't re-render The API request body can be easily built when using , whereas we would need to assemble the data for submission when using . FormData useState It the need for introducing as and when the form grows. eliminates new state variables When dealing with multiple forms, you might end up duplicating similar state variables across components, whereas can be reused easily with just a few lines of code. FormData One thing that supports out of the box is that it will handle dynamic fields automatically. i.e., If your form has (adding/removing fields based on user input), managing their state with requires additional handling, whereas will take care of it automatically. FormData dynamically generated fields useState FormData Conclusion You can check the code for this article on code sandbox . Hope you learned something new from this article. Leave a comment if you have any doubts. Thanks! here Also published . here