Master form handling in React by understanding the trade-offs between controlled components and uncontrolled components. Learn when to use state versus refs.
Previously in this course, we covered Asynchronous Data Lifecycle to handle API states. Now that our dashboard displays live data, we need to allow users to interact with it via forms. Choosing between controlled components and uncontrolled components is the fundamental decision that dictates how your form state flows.
In React, the "source of truth" for your data is almost always state. However, forms are a unique beast because the DOM itself maintains its own internal state for inputs.
Controlled components delegate that responsibility to React. You bind the value of an input to a piece of state and update it via an onChange handler. This makes the input "controlled" by React’s state machine.
Uncontrolled components let the DOM keep the state. You use a ref (as we practiced in Mastering useRef for DOM Access) to "pull" the value out of the input only when you actually need it, such as during a form submission.
Use controlled components when you need real-time validation, dynamic UI updates based on input, or conditional disabling of buttons. If the input state needs to drive other parts of the application, it must be controlled.
JSXimport { useState } from CE9178">'react'; function ControlledInput() { const [email, setEmail] = useState(CE9178">''); return ( <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} /> ); }
Because email is in our state, we can easily display an error message if the user types an invalid domain or disable a "Save" button if the field is empty.
Uncontrolled components are ideal for simple forms where you don't care about the input until the moment the user clicks "Submit." They are more performant because they don't trigger a re-render on every keystroke.
JSXimport { useRef } from CE9178">'react'; function UncontrolledInput() { const inputRef = useRef(null); const handleSubmit = () => { alert(CE9178">`Submitted value: ${inputRef.current.value}`); }; return ( <> <input type="text" ref={inputRef} /> <button onClick={handleSubmit}>Submit</button> </> ); }
In our dashboard project, we are building a settings page. Here is how to decide:
In our current dashboard, we have a search bar. Let's refactor it.
ref to grab the value only on the button click.Try implementing the uncontrolled version first to see how much code you save, then switch to controlled to feel the difference in reactivity.
value and defaultValue on the same input. React will throw a warning because it doesn't know who owns the state.ref.current during the initial render before the component has mounted.We’ve learned that controlled components provide maximum control via React state, while uncontrolled components offer a lightweight alternative using useRef. By selecting the right approach, you keep your dashboard performant and your code maintainable.
Up next: Real-time Form Validation where we will add robust error handling to these inputs.
Learn to master loading state in React to improve UX. Discover how to conditionally render spinners while your API fetches data for your movie app.
Read moreLearn to master scalability through expert refactoring techniques. We'll extract reusable UI, optimize folder structures, and decouple logic for cleaner code.
Controlled vs Uncontrolled Components
Finalizing Dashboard Data Flow
Deploying the Application
Advanced Hook Composition
Implementing Middleware for State
Advanced Context Patterns
Router Loaders and Data Prefetching
Complex Route Guards
Handling Large Datasets in UI
Testing Hooks and Components
Managing Global Modals
Implementing Keyboard Shortcuts
Optimizing Asset Loading
Internationalization Basics
Managing WebSocket Connections