useControllableState
State that works controlled or uncontrolled, with the value / defaultValue / onChange pattern.
Every Axiom input can be controlled (value + onValueChange) or manage its own state (defaultValue). useControllableState holds that logic once, for Switch, Checkbox, Slider, Accordion, Dialog and the other components with a value or an open state.
Installation
npx axiom add use-controllable-statepnpm dlx axiom add use-controllable-stateyarn dlx axiom add use-controllable-statebun x axiom add use-controllable-stateComponents that need it copy it for you.
Usage
import { useControllableState } from "@/hooks/use-controllable-state";
type RatingProps = {
value?: number;
defaultValue?: number;
onValueChange?: (value: number) => void;
};
function Rating({ value, defaultValue = 0, onValueChange }: RatingProps) {
const [rating, setRating] = useControllableState({
value,
defaultValue,
onChange: onValueChange,
});
return <Stars value={rating} onPress={setRating} />;
}Both ways work for the person using the component:
<Rating defaultValue={3} /> // uncontrolled
<Rating value={rating} onValueChange={setRating} /> // controlledAPI
const [value, setValue] = useControllableState<T>({
value,
defaultValue,
onChange,
});| Option | Type | Description |
|---|---|---|
value | T | Controlled value. When it’s set, the owner decides: setValue only calls onChange. |
defaultValue | T | Required. Initial value when value is undefined. |
onChange | (value: T) => void | Called when setValue receives a different value (Object.is). |
| Returns | Type | Description |
|---|---|---|
value | T | value when controlled, the internal state otherwise. |
setValue | (next: T) => void | Updates the internal state when uncontrolled, and calls onChange when the value changes. |
undefined means uncontrolled. To control a value that can be empty, use null or '' for the empty state, not undefined.
A component shouldn’t switch between controlled and uncontrolled while mounted. defaultValue is only read on the first render.