All hooks / Component helpers
useControllableState
State for a component that can be controlled (the parent passes value and onChange) or uncontrolled (the parent passes only a default), like a native <input> with value or defaultValue.
Returns [value, setValue] like useState. setValue calls onChange only when the value actually changes, and accepts an updater function that sees the latest value, also for several calls in one event. Controlled while value is not undefined; don't switch between the two.
Import
import { useControllableState } from '@vincent.ri/react-tils'
// or only this hook
import { useControllableState } from '@vincent.ri/react-tils/useControllableState'Usage
type Props = { open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean) => void }
function Disclosure({ open: openProp, defaultOpen = false, onOpenChange }: Props) {
const [open, setOpen] = useControllableState(openProp, defaultOpen, onOpenChange)
return <button aria-expanded={open} onClick={() => setOpen((o) => !o)}>Details</button>
}Live demo
Same component: owns its state, or the parent does.
When to use it
Building a reusable component (tabs, accordion, toggle, select, dialog open) whose parent may or may not want to own the state.
When not to
The state is only used inside one component; use useState. Never copy a prop into state with useState(prop) + useEffect to sync it: the copy goes stale or flickers. Use this hook instead.
API
function useControllableState<T>(value: T | undefined, defaultValue: T, onChange?: (value: T) => void): [T, SetValue<T>]| Parameter | Description |
|---|---|
value | The controlled value, or undefined when uncontrolled. |
defaultValue | The starting value when uncontrolled. |
onChange | Called with the new value on every change. Optional. |
Browser support
Uses only long-established browser features, so it works wherever React does.
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).
Coming from another library?
Other libraries call this useControlled, useUncontrolled, useControllableValue.