Forms in React
Forms are essential for collecting user input. React handles forms differently than traditional HTML through controlled components.
Controlled vs Uncontrolled
Controlled Components
React state is the "single source of truth":
jsx
1function ControlledInput() {
2 const [value, setValue] = useState('')
3
4 return (
5 <input
6 value={value}
7 onChange={(e) => setValue(e.target.value)}
8 />
9 )
10}Uncontrolled Components
DOM maintains its own state:
jsx
1function UncontrolledInput() {
2 const inputRef = useRef(null)
3
4 const handleSubmit = () => {
5 console.log(inputRef.current.value)
6 }
7
8 return (
9 <>
10 <input ref={inputRef} defaultValue="initial" />
11 <button onClick={handleSubmit}>Submit</button>
12 </>
13 )
14}Basic Form Elements
Text Input
jsx
1function TextInput() {
2 const [name, setName] = useState('')
3
4 return (
5 <div>
6 <label htmlFor="name">Name:</label>
7 <input
8 id="name"
9 type="text"
10 value={name}
11 onChange={(e) => setName(e.target.value)}
12 placeholder="Enter your name"
13 />
14 <p>Hello, {name || 'stranger'}!</p>
15 </div>
16 )
17}Textarea
jsx
1function TextArea() {
2 const [bio, setBio] = useState('')
3
4 return (
5 <div>
6 <label htmlFor="bio">Bio:</label>
7 <textarea
8 id="bio"
9 value={bio}
10 onChange={(e) => setBio(e.target.value)}
11 rows={4}
12 placeholder="Tell us about yourself"
13 />
14 <p>{bio.length}/500 characters</p>
15 </div>
16 )
17}Select Dropdown
jsx
1function SelectInput() {
2 const [country, setCountry] = useState('')
3
4 return (
5 <div>
6 <label htmlFor="country">Country:</label>
7 <select
8 id="country"
9 value={country}
10 onChange={(e) => setCountry(e.target.value)}
11 >
12 <option value="">Select a country</option>
13 <option value="us">United States</option>
14 <option value="uk">United Kingdom</option>
15 <option value="ca">Canada</option>
16 </select>
17 </div>
18 )
19}Checkbox
jsx
1function Checkbox() {
2 const [agreed, setAgreed] = useState(false)
3
4 return (
5 <div>
6 <label>
7 <input
8 type="checkbox"
9 checked={agreed}
10 onChange={(e) => setAgreed(e.target.checked)}
11 />
12 I agree to the terms
13 </label>
14 </div>
15 )
16}
17
18// Multiple checkboxes
19function MultiCheckbox() {
20 const [selected, setSelected] = useState([])
21
22 const toggleOption = (option) => {
23 setSelected(prev =>
24 prev.includes(option)
25 ? prev.filter(o => o !== option)
26 : [...prev, option]
27 )
28 }
29
30 return (
31 <div>
32 {['React', 'Vue', 'Angular'].map(option => (
33 <label key={option}>
34 <input
35 type="checkbox"
36 checked={selected.includes(option)}
37 onChange={() => toggleOption(option)}
38 />
39 {option}
40 </label>
41 ))}
42 </div>
43 )
44}Radio Buttons
jsx
1function RadioGroup() {
2 const [size, setSize] = useState('medium')
3
4 return (
5 <div>
6 {['small', 'medium', 'large'].map(option => (
7 <label key={option}>
8 <input
9 type="radio"
10 name="size"
11 value={option}
12 checked={size === option}
13 onChange={(e) => setSize(e.target.value)}
14 />
15 {option.charAt(0).toUpperCase() + option.slice(1)}
16 </label>
17 ))}
18 </div>
19 )
20}Complete Form Example
jsx
1function RegistrationForm() {
2 const [form, setForm] = useState({
3 firstName: '',
4 lastName: '',
5 email: '',
6 password: '',
7 country: '',
8 newsletter: false
9 })
10 const [errors, setErrors] = useState({})
11
12 const handleChange = (e) => {
13 const { name, value, type, checked } = e.target
14 setForm(prev => ({
15 ...prev,
16 [name]: type === 'checkbox' ? checked : value
17 }))
18 }
19
20 const validate = () => {
21 const newErrors = {}
22
23 if (!form.firstName.trim()) {
24 newErrors.firstName = 'First name is required'
25 }
26
27 if (!form.email.trim()) {
28 newErrors.email = 'Email is required'
29 } else if (!/\S+@\S+\.\S+/.test(form.email)) {
30 newErrors.email = 'Email is invalid'
31 }
32
33 if (!form.password) {
34 newErrors.password = 'Password is required'
35 } else if (form.password.length < 8) {
36 newErrors.password = 'Password must be at least 8 characters'
37 }
38
39 setErrors(newErrors)
40 return Object.keys(newErrors).length === 0
41 }
42
43 const handleSubmit = (e) => {
44 e.preventDefault()
45
46 if (validate()) {
47 console.log('Form submitted:', form)
48 // Submit to API
49 }
50 }
51
52 return (
53 <form onSubmit={handleSubmit}>
54 <div>
55 <label htmlFor="firstName">First Name</label>
56 <input
57 id="firstName"
58 name="firstName"
59 value={form.firstName}
60 onChange={handleChange}
61 />
62 {errors.firstName && <span className="error">{errors.firstName}</span>}
63 </div>
64
65 <div>
66 <label htmlFor="lastName">Last Name</label>
67 <input
68 id="lastName"
69 name="lastName"
70 value={form.lastName}
71 onChange={handleChange}
72 />
73 </div>
74
75 <div>
76 <label htmlFor="email">Email</label>
77 <input
78 id="email"
79 name="email"
80 type="email"
81 value={form.email}
82 onChange={handleChange}
83 />
84 {errors.email && <span className="error">{errors.email}</span>}
85 </div>
86
87 <div>
88 <label htmlFor="password">Password</label>
89 <input
90 id="password"
91 name="password"
92 type="password"
93 value={form.password}
94 onChange={handleChange}
95 />
96 {errors.password && <span className="error">{errors.password}</span>}
97 </div>
98
99 <div>
100 <label htmlFor="country">Country</label>
101 <select
102 id="country"
103 name="country"
104 value={form.country}
105 onChange={handleChange}
106 >
107 <option value="">Select country</option>
108 <option value="us">United States</option>
109 <option value="uk">United Kingdom</option>
110 </select>
111 </div>
112
113 <div>
114 <label>
115 <input
116 type="checkbox"
117 name="newsletter"
118 checked={form.newsletter}
119 onChange={handleChange}
120 />
121 Subscribe to newsletter
122 </label>
123 </div>
124
125 <button type="submit">Register</button>
126 </form>
127 )
128}File Input
jsx
1function FileUpload() {
2 const [file, setFile] = useState(null)
3 const [preview, setPreview] = useState('')
4
5 const handleFileChange = (e) => {
6 const selectedFile = e.target.files[0]
7 setFile(selectedFile)
8
9 // Create preview for images
10 if (selectedFile?.type.startsWith('image/')) {
11 const reader = new FileReader()
12 reader.onloadend = () => setPreview(reader.result)
13 reader.readAsDataURL(selectedFile)
14 }
15 }
16
17 return (
18 <div>
19 <input
20 type="file"
21 accept="image/*"
22 onChange={handleFileChange}
23 />
24 {preview && <img src={preview} alt="Preview" width={200} />}
25 {file && <p>Selected: {file.name}</p>}
26 </div>
27 )
28}Form Reset
jsx
1function ResettableForm() {
2 const initialState = { name: '', email: '' }
3 const [form, setForm] = useState(initialState)
4
5 const handleReset = () => {
6 setForm(initialState)
7 }
8
9 return (
10 <form>
11 <input
12 name="name"
13 value={form.name}
14 onChange={e => setForm({...form, name: e.target.value})}
15 />
16 <input
17 name="email"
18 value={form.email}
19 onChange={e => setForm({...form, email: e.target.value})}
20 />
21 <button type="button" onClick={handleReset}>Reset</button>
22 <button type="submit">Submit</button>
23 </form>
24 )
25}Forms are the primary way users interact with your React applications!
