React Forms
Controlled inputs keep an input element's value in sync with React state, so state is always the single source of truth for what's on screen.
Controlled Inputs with value and onChange
A controlled input sets its value from state and updates that state through onChange on every keystroke. Because the input's displayed value always comes from state, React can validate, transform, or reset the value at any time — the DOM never holds a value React doesn't know about.
A Single Controlled Text Input
import { useState } from 'react';
function NameInput() {
const [name, setName] = useState('');
return (
<input
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
/>
);
}
export default NameInput;
Handling Multiple Fields with One Handler
Rather than writing a separate handler for every field, give each input a name attribute matching a key in a single state object, then use one handleChange function that reads event.target.name to know which key to update. This scales cleanly as forms grow past two or three fields.
One Handler, Many Fields
import { useState } from 'react';
function SignupForm() {
const [form, setForm] = useState({ email: '', password: '' });
function handleChange(event) {
const { name, value } = event.target;
setForm((prev) => ({ ...prev, [name]: value }));
}
return (
<form>
<input name="email" value={form.email} onChange={handleChange} />
<input
name="password"
type="password"
value={form.password}
onChange={handleChange}
/>
</form>
);
}
export default SignupForm;
Submitting Forms and preventDefault
By default, submitting an HTML form reloads the page — the browser sends a request and throws away all your JavaScript state. Calling event.preventDefault() inside your onSubmit handler stops that default navigation so you can handle the submitted data entirely in React instead.
A Complete Submit Flow
import { useState } from 'react';
function ContactForm() {
const [message, setMessage] = useState('');
const [sent, setSent] = useState(false);
function handleSubmit(event) {
event.preventDefault();
console.log('Sending:', message);
setSent(true);
}
if (sent) {
return <p>Thanks — your message was sent!</p>;
}
return (
<form onSubmit={handleSubmit}>
<textarea
value={message}
onChange={(event) => setMessage(event.target.value)}
/>
<button type="submit">Send</button>
</form>
);
}
export default ContactForm;
- Always pair value with onChange — a value prop without onChange makes React warn about a read-only field.
- Call event.preventDefault() as the first line of your onSubmit handler.
- Use a single object in state with computed [name]: value updates once a form has more than two or three fields.
- Disable or hide the submit button while an async submission is in flight to prevent duplicate submits.
Exercise: React Forms
In a controlled input, what makes React the 'single source of truth' for the field's value?