Function: useControlledState()
useControlledState<
T>(value,defaultValue,onChange?): readonly [T, (newValue) =>void]
Defined in: use-controlled-state.ts:90
A custom React hook that implements the controlled/uncontrolled component pattern.
This hook allows components to work in two modes:
- Controlled mode: When
valueis provided, the parent component manages the state - Uncontrolled mode: When
valueis undefined, the hook manages state internally
This pattern provides flexibility for component consumers, allowing them to choose between full control of the state or letting the component manage it automatically.
The setValue function is optimized for performance with a stable reference that only
changes when the component switches between controlled/uncontrolled modes or when the
onChange callback changes. This prevents unnecessary re-renders in child components.
In development mode, the hook will warn if a component switches between controlled and uncontrolled modes, which is considered an anti-pattern and can lead to unexpected behavior.
Type Parameters
T
T
The type of the state value.
Parameters
value
T | undefined
The controlled value. When provided, the hook operates in controlled mode.
defaultValue
T | (() => T)
The initial value for uncontrolled mode. Can be a value or lazy initializer function.
onChange?
(value) => void
Optional callback invoked whenever the value changes.
Returns
readonly [T, (newValue) => void]
A tuple containing the current value and a stable setter function that accepts either a new value or an updater function.
Examples
// Uncontrolled usage - component manages its own state
function UncontrolledInput() {
const [value, setValue] = useControlledState(undefined, "", console.log);
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
}
// Controlled usage - parent manages the state
function ControlledInput({ value, onChange }) {
const [inputValue, setInputValue] = useControlledState(value, "", onChange);
return <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />;
}
// With updater function for complex state updates
const [count, setCount] = useControlledState(undefined, 0);
setCount(prev => prev + 1);
// With lazy initialization for expensive defaults
const [data, setData] = useControlledState(
undefined,
() => expensiveComputation()
);