跪拜 Guibai
← All articles
Frontend

React Form Data Flow: When State Should Own the Input and When the DOM Should

By 东风破_ ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Mispicking the pattern leads to unresponsive inputs, stale validation, or unnecessary re-renders. Knowing when to let the DOM hold the value avoids over-engineering simple forms and keeps complex ones predictable.

Summary

A controlled input binds `value` to React state and updates it via `onChange`, making state the single source of truth. An uncontrolled input skips the binding and stores the value directly in the DOM, retrieved through a ref only when needed. The difference determines whether real-time validation, field-to-field dependencies, and button states are straightforward or require manual synchronization. Multi-field forms can share a single change handler by matching each input's `name` attribute to a state key, using computed property names to update the correct field. Real-time validation becomes a natural side effect: the same state that drives the input also feeds error messages and disables the submit button. File inputs remain a hard exception because browsers block scripted value assignment, so they stay uncontrolled. Mixing modes within a single form is practical, but toggling a single input between controlled and uncontrolled across renders causes bugs. The decision hinges on when the value is consumed: if it feeds UI logic on every change, control it; if it's only needed at submit, let the DOM keep it.

Takeaways
— Controlled inputs pair `value` (or `checked`) with `onChange` so React state is the single source of truth.
— Uncontrolled inputs store the value in the DOM and retrieve it via `ref.current.value`, typically at submit time.
— Setting `value` without an `onChange` handler locks the input because React re-renders with the old state value.
— Use `defaultValue` for initial content in uncontrolled inputs; switching to `value` later flips the component into controlled mode.
— Multiple fields can share one change handler by giving each input a `name` that matches a state key and using `[name]: value` in the updater.
— Real-time validation works cleanly with controlled forms because the same state drives inputs, error messages, and button disabled states.
— File inputs are always uncontrolled because browsers forbid scripted value assignment for security reasons.
— Never let a single input switch between controlled and uncontrolled across renders; initialize state with the correct type (string, boolean) from the start.
Conclusions

A form doesn't need to be uniformly controlled or uncontrolled; text fields can be controlled while a file picker stays uncontrolled, and that hybrid is often the cleanest real-world pattern.

The practical litmus test is whether you find yourself writing `ref.current.value` only to immediately feed it into `setState` — if so, the component should have been controlled from the start.

React's insistence on `value` + `onChange` as a pair is less about ceremony and more about preventing the class of bugs where the DOM and React's virtual representation silently diverge.

Concepts & terms
Controlled component
A form element whose value is driven by React state. The component receives its current value as a prop and notifies changes via a callback like `onChange`, making React the single source of truth.
Uncontrolled component
A form element whose value is stored in the DOM itself. React reads the value imperatively through a ref, typically only when needed (e.g., on submit), rather than on every keystroke.
Computed property name
A JavaScript syntax (`[expression]: value`) inside an object literal that evaluates the expression to determine the property key. In forms, it lets a single change handler update the correct state field by using the input's `name` attribute as the key.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗