Frontend Development 6 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Understanding Controlled vs Uncontrolled Components and Implementing the usePropsValue Hook in React

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.

performance optimizationreactCustom HookControlled ComponentUncontrolled Component
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.