Forms
Controlled vs Uncontrolled Components
React offers two approaches for managing form inputs:
| Approach | Description |
|---|---|
| Controlled | The input value is controlled by React state. Recommended. |
| Uncontrolled | The input value is managed by the DOM itself. |
Controlled Components
Every input has a value bound to React state. Each change is updated into state.
import { useState } from 'react';
function FormLogin() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
return (
<form onSubmit={(e) => { e.preventDefault(); console.log(email, password); }}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Log In</button>
</form>
);
}
Uncontrolled Components
Use useRef to access the DOM value directly without state on every keystroke.
import { useRef } from 'react';
function FormUncontrolled() {
const nameRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log("Name:", nameRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} type="text" placeholder="Name" />
<button type="submit">Send</button>
</form>
);
}
Text Inputs and Textareas
function FormProfile() {
const [form, setForm] = useState({ name: "", bio: "" });
const handleChange = (e) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
return (
<form>
<input
name="name"
value={form.name}
onChange={handleChange}
placeholder="Full name"
/>
<textarea
name="bio"
value={form.bio}
onChange={handleChange}
placeholder="Tell us about yourself"
/>
<p>{form.bio.length}/200 characters</p>
</form>
);
}
Select (Dropdown)
function FormSelect() {
const [language, setLanguage] = useState("id");
return (
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="id">Indonesia</option>
<option value="en">English</option>
<option value="ja">日本語</option>
</select>
);
}
Checkbox
function FormCheckbox() {
const [agreed, setAgreed] = useState(false);
return (
<label>
<input
type="checkbox"
checked={agreed}
onChange={(e) => setAgreed(e.target.checked)}
/>
I agree to the terms and conditions
</label>
);
}
Radio Button
function FormRadio() {
const [gender, setGender] = useState("male");
return (
<div>
<label>
<input
type="radio"
name="gender"
value="male"
checked={gender === "male"}
onChange={(e) => setGender(e.target.value)}
/>
Male
</label>
<label>
<input
type="radio"
name="gender"
value="female"
checked={gender === "female"}
onChange={(e) => setGender(e.target.value)}
/>
Female
</label>
<p>Selection: {gender}</p>
</div>
);
}
Form Submission Handler
function RegistrationForm() {
const [form, setForm] = useState({ name: "", email: "", agree: false });
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setForm({ ...form, [name]: type === "checkbox" ? checked : value });
};
const handleSubmit = (e) => {
e.preventDefault();
if (!form.name || !form.email) {
alert("Name and email are required!");
return;
}
if (!form.agree) {
alert("You must agree to the terms!");
return;
}
console.log("Data submitted:", form);
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={form.name} onChange={handleChange} placeholder="Name" />
<input name="email" value={form.email} onChange={handleChange} placeholder="Email" />
<label>
<input
name="agree"
type="checkbox"
checked={form.agree}
onChange={handleChange}
/>
I agree
</label>
<button type="submit">Register</button>
</form>
);
}
export default RegistrationForm;
Summary
Controlled components are the standard approach in React for managing forms. By binding every input to state, you get full control over the data, validation, and form behavior. Combine it with event handling for a complete form experience.
Congratulations! You have finished the React fundamentals. Next steps: learn React Router for navigation, Redux or Context API for state management, and start building real projects.