← Back to Chapters

Forms Handling in React

? Forms Handling in React

? Topic: React Forms ⚙️ Controlled Components

⚡ Quick Overview

Forms are an essential part of every web application — they collect user input such as text, emails, passwords, and selections. In React, form elements like <input>, <textarea>, and <select> are usually controlled using component state.

By controlling form data through state, React makes it easier to:

  • Read and store user input safely.
  • Validate data before submission.
  • Show real-time feedback and error messages.
  • Keep UI and data in perfect sync.

? Key Concepts

  • Controlled Components: Form inputs whose values are managed by React state using hooks like useState().
  • Uncontrolled Components: Inputs that manage their own state in the DOM, accessed using ref. React recommends controlled components for most cases.
  • Event Handlers: onChange to update state on each keystroke, and onSubmit to process or validate data when the form is submitted.
  • Multiple Fields: One state object (like formData) can store many fields, making complex forms easier to manage.
  • Validation: Check state values and show messages before sending data to a server.

? Syntax & Theory

In React, the value of an input is usually bound to a state variable:

  • Use useState() to create a state variable for each field or a combined object.
  • Attach an onChange handler to the input to update state whenever the user types or selects.
  • Handle form submission using the onSubmit event on the <form> element.
  • Call e.preventDefault() in onSubmit to stop the browser’s default page refresh.

? Example 1: Simple Controlled Form

In a controlled form, each input element’s value is synced with component state using useState(). React always “knows” the current value of the inputs.

? View Code Example
// Controlled form with name and email fields
import React, { useState } from "react";

function SimpleForm() {
const [name, setName] = useState("");
const [email, setEmail] = useState("");

const handleSubmit = (e) => {
e.preventDefault();
// Show the collected form data on submit
alert(`Name: ${name}\nEmail: ${email}`);
};

return (
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label>Name:</label>
<input
type="text"
className="form-control"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>

<div className="mb-3">
<label>Email:</label>
<input
type="email"
className="form-control"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>

<button className="btn btn-primary">Submit</button>
</form>
);
}

export default SimpleForm;

? What This Does

  • Typing in the inputs updates name and email state instantly.
  • Submitting the form triggers handleSubmit.
  • The browser does not refresh (because of e.preventDefault()).
  • An alert shows the entered name and email.

? Example 2: Handling Multiple Inputs

Instead of managing each input separately, you can store all field values in a single state object and update it using the input’s name.

? View Code Example
// Single state object to manage multiple fields
function MultiInputForm() {
const [formData, setFormData] = React.useState({
username: "",
password: "",
});

const handleChange = (e) => {
const { name, value } = e.target;
// Copy existing formData and override only the changed field
setFormData({ ...formData, [name]: value });
};

const handleSubmit = (e) => {
e.preventDefault();
// Log the entire form object for debugging or sending to an API
console.log(formData);
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
placeholder="Username"
className="form-control mb-2"
value={formData.username}
onChange={handleChange}
/>

<input
type="password"
name="password"
placeholder="Password"
className="form-control mb-2"
value={formData.password}
onChange={handleChange}
/>

<button className="btn btn-success">Login</button>
</form>
);
}

? What This Does

  • All fields live inside a single formData object.
  • handleChange updates the right key based on name.
  • On submit, you can send formData directly to an API.

? Example 3: Select and Textarea Inputs

React treats <select> and <textarea> just like other inputs — their value is controlled by state and updated with onChange.

? View Code Example
// Form with select dropdown and textarea message
function ExtendedForm() {
const [form, setForm] = React.useState({
country: "India",
message: "",
});

const handleChange = (e) => {
const { name, value } = e.target;
// Keep the rest of the object and update only the changed field
setForm({ ...form, [name]: value });
};

const handleSubmit = (e) => {
e.preventDefault();
// Show the full form object as pretty JSON
alert(JSON.stringify(form, null, 2));
};

return (
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label>Country:</label>
<select
name="country"
className="form-select"
value={form.country}
onChange={handleChange}
>
<option>India</option>
<option>USA</option>
<option>Germany</option>
</select>
</div>

<div className="mb-3">
<label>Message:</label>
<textarea
name="message"
className="form-control"
rows="3"
value={form.message}
onChange={handleChange}
/>
</div>

<button className="btn btn-info">Submit</button>
</form>
);
}

? What This Does

  • The dropdown always shows the current country from state.
  • The textarea always reflects message from state.
  • On submit, an alert displays the full form object.

⚙️ Example 4: Simple Form Validation

You can easily validate user input by checking state values and showing error messages when the user submits invalid data.

? View Code Example
// Basic email validation using component state
function ValidatedForm() {
const [email, setEmail] = React.useState("");
const [error, setError] = React.useState("");

const handleSubmit = (e) => {
e.preventDefault();
// Very simple check to ensure email contains '@'
if (!email.includes("@")) {
setError("Please enter a valid email address.");
return;
}
alert("Email submitted successfully!");
setError("");
};

return (
<form onSubmit={handleSubmit}>
<input
type="email"
className="form-control mb-2"
placeholder="Enter email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>

{error && <p className="text-danger">{error}</p>}

<button className="btn btn-warning">Submit</button>
</form>
);
}

? What This Does

  • The user types an email into the input.
  • On submit, the code checks if the email contains "@".
  • If invalid, an error message is shown below the input.
  • If valid, a success alert appears and the error is cleared.

? Common Patterns in React Forms

  • Use one state object to store multiple fields (e.g., formData).
  • Always call e.preventDefault() inside onSubmit to stop page refresh.
  • Use onChange to sync user input with state on every keystroke.
  • Use onSubmit for validation, API calls, and data handling.
  • Prefer controlled inputs to keep UI in sync with your application’s state.

? Tips & Best Practices

  • Initialize all form fields in state to avoid uncontrolled-to-controlled warnings.
  • Group related fields into a single state object for cleaner, scalable code.
  • Use form libraries like Formik or React Hook Form for large or complex forms.
  • Use the HTML required attribute for basic validation on inputs.
  • Show helpful error messages close to the relevant input fields.

? Try It Yourself

  1. Create a login form with username and password using controlled components.
  2. Add validation to ensure no fields are empty before submission.
  3. Build a contact form with a <textarea> for message and a <select> for topic.
  4. Display the current form data dynamically below the form as the user types.

Goal: Understand how to handle user inputs, control form values through state, and validate form submissions in React efficiently.