use-controllable-state.ts


Overview

The use-controllable-state.ts file provides a custom React hook, useControllableState, designed to manage state that can be either controlled or uncontrolled. This pattern is common in component libraries where a component's state can be controlled externally via props or managed internally by the component itself.

The hook abstracts the logic to switch between controlled and uncontrolled modes seamlessly, ensuring callback synchronization when the state changes. It improves component flexibility and reusability by allowing state management to be customized based on usage context.


Exports

useControllableState

function useControllableState<T>(params: UseControllableStateParams<T>): readonly [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>];

A custom React hook that manages a state value which can either be controlled externally (via the prop parameter) or uncontrolled internally (using defaultProp). It also accepts an onChange callback that fires when the state changes.


Types

UseControllableStateParams<T>

Parameters object for useControllableState hook.

Property

Type

Description

prop

`T \

undefined` (optional)

defaultProp

`T \

undefined` (optional)

onChange

(state: T) => void (optional)

Callback invoked when the state changes.


SetStateFn<T>

type SetStateFn<T> = (prevState?: T) => T;

A function type used to update state based on previous state, similar to the updater function in React’s setState.


Functions

useUncontrolledState

function useUncontrolledState<T>({
  defaultProp,
  onChange,
}: Omit<UseControllableStateParams<T>, 'prop'>): [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>];

A helper hook used internally by useControllableState to manage uncontrolled state.

const [value, setValue] = useUncontrolledState({
  defaultProp: 'default',
  onChange: (val) => console.log('Changed to', val),
});

useControllableState

function useControllableState<T>({
  prop,
  defaultProp,
  onChange = () => {},
}: UseControllableStateParams<T>): readonly [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>];

Main hook exported by the file. Manages a state that can be controlled externally or internally.

// Controlled usage
const [value, setValue] = useControllableState<string>({
  prop: controlledValue,
  onChange: (val) => console.log('Controlled changed to', val),
});

// Uncontrolled usage
const [value, setValue] = useControllableState<string>({
  defaultProp: 'initial',
  onChange: (val) => console.log('Uncontrolled changed to', val),
});

Important Implementation Details


Interactions with Other Parts of the System


Visual Diagram

flowchart TD
    A[useControllableState<T>] --> B{Is prop defined?}
    B -- Yes --> C[Controlled Mode]
    B -- No --> D[Uncontrolled Mode]
    D --> E[useUncontrolledState<T>]
    E --> F[useState<T | undefined>]
    E --> G[useEffect calls onChange on state change]
    C --> H[Returns prop as value]
    D --> I[Returns internal state as value]
    A --> J[setValue function]
    J --> K{Controlled?}
    K -- Yes --> L[Call onChange if value differs]
    K -- No --> M[Update internal state via setUncontrolledProp]

Summary

This file exports a single, versatile React hook useControllableState that simplifies dual-mode (controlled/uncontrolled) state management for React components. It abstracts the complexity of managing internal state vs external props and synchronizes all changes with an optional onChange callback. The hook leverages React's hooks and a stable callback ref utility to ensure consistent behavior and performance.

This utility is essential in component libraries that want to provide flexible APIs allowing consumers to choose their preferred state management style without rewriting component logic.