Controlled vs Uncontrolled Components
The foundation of React form handling lies in understanding the fundamental difference between controlled and uncontrolled components. Controlled components keep form data in React state, giving you complete control over the input values and enabling real-time validation, conditional rendering, and dynamic form behavior. Uncontrolled components, on the other hand, let the DOM handle form data, which can be simpler for basic use cases and is necessary for file inputs. Mastering both approaches allows you to choose the right tool for each situation and build more efficient, maintainable forms.
Controlled Component
1import { useState } from 'react';23const ControlledForm = () => {4 const [formData, setFormData] = useState({5 username: '',6 email: '',7 agreeToTerms: false8 });9 const [errors, setErrors] = useState({});1011 const handleChange = (e) => {12 const { name, value, type, checked } = e.target;13 setFormData(prev => ({14 ...prev,15 [name]: type === 'checkbox' ? checked : value16 }));17 };1819 const validate = () => {20 const newErrors = {};21 if (!formData.username) newErrors.username = 'Username is required';22 if (!formData.email) newErrors.email = 'Email is required';23 else if (!/S+@S+.S+/.test(formData.email)) newErrors.email = 'Invalid email';24 if (!formData.agreeToTerms) newErrors.agreeToTerms = 'Must agree to terms';25 return newErrors;26 };2728 const handleSubmit = (e) => {29 e.preventDefault();30 const newErrors = validate();31 setErrors(newErrors);3233 if (Object.keys(newErrors).length === 0) {34 console.log('Form submitted:', formData);35 }36 };3738 return (39 <form onSubmit={handleSubmit}>40 <input41 type="text"42 name="username"43 placeholder="Username"44 value={formData.username}45 onChange={handleChange}46 />47 {errors.username && <span className="error">{errors.username}</span>}4849 <input50 type="email"51 name="email"52 placeholder="Email"53 value={formData.email}54 onChange={handleChange}55 />56 {errors.email && <span className="error">{errors.email}</span>}5758 <label>59 <input60 type="checkbox"61 name="agreeToTerms"62 checked={formData.agreeToTerms}63 onChange={handleChange}64 />65 I agree to terms66 </label>67 {errors.agreeToTerms && <span className="error">{errors.agreeToTerms}</span>}6869 <button type="submit">Submit</button>70 </form>71 );72};
Uncontrolled Component
1import { useRef } from 'react';23const UncontrolledForm = () => {4 const usernameRef = useRef();5 const emailRef = useRef();6 const fileInputRef = useRef();78 const handleSubmit = (e) => {9 e.preventDefault();1011 const formData = {12 username: usernameRef.current.value,13 email: emailRef.current.value,14 file: fileInputRef.current.files[0]15 };1617 console.log('Form data:', formData);18 };1920 return (21 <form onSubmit={handleSubmit}>22 <input23 ref={usernameRef}24 type="text"25 name="username"26 placeholder="Username"27 defaultValue=""28 />2930 <input31 ref={emailRef}32 type="email"33 name="email"34 placeholder="Email"35 defaultValue=""36 />3738 <input39 ref={fileInputRef}40 type="file"41 name="file"42 accept="image/*"43 />4445 <button type="submit">Submit</button>46 </form>47 );48};4950// Hybrid approach51const HybridForm = () => {52 const [email, setEmail] = useState('');53 const fileRef = useRef();5455 const handleSubmit = (e) => {56 e.preventDefault();57 console.log({58 email,59 file: fileRef.current.files[0]60 });61 };6263 return (64 <form onSubmit={handleSubmit}>65 <input66 type="email"67 value={email}68 onChange={(e) => setEmail(e.target.value)}69 placeholder="Email (controlled)"70 />71 <input72 ref={fileRef}73 type="file"74 placeholder="File (uncontrolled)"75 />76 <button type="submit">Submit</button>77 </form>78 );79};
Comparison Table
Feature | Controlled | Uncontrolled |
---|---|---|
Form data storage | React state | DOM |
Real-time validation | Easy | Complex |
Dynamic forms | Simple | Difficult |
File inputs | Not possible | Natural |
Performance | Re-renders on change | No re-renders |
Testing | Easier | Requires DOM |