Understanding Controlled vs Uncontrolled Components and Implementing the usePropsValue Hook in React
The article explains the difference between controlled and uncontrolled React components, introduces the usePropsValue custom hook to unify their handling, provides implementation details with code examples, and discusses optimization techniques to reduce unnecessary re-renders.
This article discusses the concepts of controlled and uncontrolled components in React, emphasizing that ignoring these concepts can lead to excessive re‑rendering.
Controlled components are those whose value is directly managed by code, typically using value and onChange props (e.g., an Input whose value is set to e.target.value ).
Uncontrolled components rely on the DOM to hold their state; they are initialized with defaultValue and accessed via ref or form submission, without explicit onChange handling.
Understanding when to use each pattern helps avoid unnecessary state updates and renders, especially for forms, switches, calendars, and similar UI elements.
The usePropsValue hook abstracts this decision by accepting value , defaultValue , and onChange and internally determining whether the component should behave in a controlled or uncontrolled manner.
Typical usage looks like:
const [selfValue, setSelfValue] = usePropsValue({
value: props.value,
defaultValue: props.defaultValue,
onChange: props.onChange
});The hook’s implementation (simplified) is:
type Options
= {
value?: T;
defaultValue: T;
onChange?: (v: T) => void;
};
export const usePropsValue =
(options: Options
) => {
const { value, defaultValue, onChange } = options;
const stateRef = useRef
(value !== undefined ? value : defaultValue);
if (value !== undefined) {
stateRef.current = value;
}
const update = useUpdate();
const setState = (v: SetStateAction
) => {
const nextValue = typeof v === 'function'
? (v as (preState: T) => T)(stateRef.current)
: v;
if (nextValue === stateRef.current) return;
stateRef.current = nextValue;
update();
onChange?.(nextValue);
};
return [stateRef.current, setState] as const;
};Optimization tips include comparing the new value with the current one to avoid redundant updates and wrapping setState with stable callbacks such as useMemoizedFn or useCallback to prevent unnecessary re‑renders of dependent components or effects.
In practice, the hook can be applied to any component, for example a Switch component that now uses usePropsValue for its internal state management. The source code is available on GitHub.
Overall, leveraging open‑source hooks like usePropsValue promotes reusable, maintainable component design and helps developers focus on functionality rather than the intricacies of controlled versus uncontrolled patterns.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.