A typical React form pairs controlled inputs, whose value comes from state and whose onChange handler updates that state, with an onSubmit handler on the form itself that calls event.preventDefault() to stop the browser's default full-page submission, then processes the current state values however the application needs, like sending them to an API. For a form with several fields, it's common to store all the field values together in a single state object, updating just the relevant key on each field's change, rather than one separate useState call per field.
1Understanding Form Handling
A typical React form pairs controlled inputs, whose value comes from state and whose onChange handler updates that state, with an onSubmit handler on the form itself that calls event.preventDefault() to stop the browser's default full-page submission, then processes the current state values however the application needs, like sending them to an API. For a form with several fields, it's common to store all the field values together in a single state object, updating just the relevant key on each field's change, rather than one separate useState call per field.
For a form with many fields, store them together as one state object and update it with the spread operator, setForm({ ...form, [name]: value }), keyed by each input's name attribute — this scales far better than one separate useState call per field as a form grows.
import { useState } from 'react';
function ContactForm() {
const [form, setForm] = useState({ name: '', email: '' });
const handleChange = (e) => setForm({ ...form, [e.target.name]: e.target.value });
const handleSubmit = (e) => {
e.preventDefault();
console.log('Submitting:', form);
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<button type="submit">Send</button>
</form>
);
}2Practical Example
Here is a real-world application of Form Handling showing how it is used in production React code.
// Typing 'A' into the name field triggers handleChange with e.target.name === 'name'
// and updates only that key, leaving email untouched
console.log({ ...{ name: '', email: '' }, name: 'A' });3Best Practices
Follow these guidelines when working with Form Handling:
1. Store multiple related form fields together in one state object, updating the changed field via its name attribute and the spread operator, rather than one useState call per field
2. Call event.preventDefault() at the start of the onSubmit handler to prevent the browser's default full-page form submission
3. Validate and process form data only after preventDefault() has stopped the default submission, keeping all the actual submission logic in JavaScript
Tip: For a form with many fields, store them together as one state object and update it with the spread operator, setForm({ ...form, [name]: value }), keyed by each input's name attribute — this scales far better than one separate useState call per field as a form grows.
import { useState } from 'react';
function ContactForm() {
const [form, setForm] = useState({ name: '', email: '' });
const handleChange = (e) => setForm({ ...form, [e.target.name]: e.target.value });
const handleSubmit = (e) => {
e.preventDefault();
console.log('Submitting:', form);
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<button type="submit">Send</button>
</form>
);
}