Skip to content
Protocore Design Systemv1.6.9

/// hooks

useControllableState

The single source of the controlled/uncontrolled behaviour every PDS input shares. Pass value to control the component; omit it and pass defaultValue to let the hook own the state. Either way, onChange fires on every change.

Signature

useControllableState
function useControllableState<T>(params: {
  value?: T;
  defaultValue?: T;
  onChange?: (value: T) => void;
}): [T, (next: T | ((prev: T) => T)) => void];

Parameters

ParameterTypeDescription
valueT | undefinedThe controlled value. When defined, the component is controlled.
defaultValueT | undefinedThe initial value in uncontrolled mode.
onChange(value: T) => voidCalled whenever the value changes, in both modes.

Returns

A [value, setValue] tuple, like useState. setValue accepts a value or an updater.

FieldTypeDescription
[0] valueTThe current value — the controlled prop when provided, else internal state.
[1] setValue(next) => voidSets the value. In controlled mode it skips internal state and only fires onChange.

When to use

  • Building your own input, toggle, or disclosure component that should support both controlled and uncontrolled use without duplicating logic.
  • Matching the PDS API convention (value / defaultValue / onValueChange) so your component composes like the built-ins.
  • You do not need it for a purely controlled or purely uncontrolled component — plain useState is simpler there.

Example

a dual-mode toggle
import { useControllableState } from "@protocore/pds";

function Toggle({ pressed, defaultPressed, onPressedChange }) {
  const [on, setOn] = useControllableState({
    value: pressed,
    defaultValue: defaultPressed ?? false,
    onChange: onPressedChange,
  });

  return (
    <button type="button" aria-pressed={on} onClick={() => setOn((p) => !p)}>
      {on ? "On" : "Off"}
    </button>
  );
}
Note
In controlled mode the hook never writes internal state — your parent’s value is the single source of truth, and setValue only calls onChange. Re-render with the new prop to reflect the change.