useBoolean IILoading saved progress…

useBoolean II

A useBoolean hook already exists: it wraps a single boolean in useState and hands back value plus three actions — setTrue, setFalse, and toggle. The base version works, but it recreates those three functions on every render, so a child wrapped in React.memo re-renders every time its parent does, and any effect that lists a setter in its dependency array re-runs needlessly. This version fixes that: the returned setTrue, setFalse, and toggle must keep the same function identity (===) across every render, while behaving exactly like the base hook.

Signature

function useBooleanIi(initialValue?: boolean): {
  value: boolean;
  setTrue: () => void;
  setFalse: () => void;
  toggle: () => void;
};

initialValue defaults to false. The three returned functions must be referentially stable — the exact same function object on render 1, render 2, and render 100.

Examples

function Disclosure() {
  const { value, setTrue, setFalse, toggle } = useBooleanIi(false);
  return (
    <div>
      <button onClick={toggle}>{value ? 'Hide' : 'Show'}</button>
      {/* Panel is memoized: because onClose is stable, it won't
          re-render when value flips somewhere else. */}
      <Panel open={value} onClose={setFalse} />
    </div>
  );
}
// Identity is preserved across renders:
const { result, rerender } = renderHook(() => useBooleanIi());
const before = result.current.toggle;
rerender();
result.current.toggle === before; // true

// Behavior still works — toggling twice in one batch is a no-op:
toggle();
toggle();
// value is unchanged

Notes

  • Identity is the whole point. setTrue, setFalse, and toggle must be the same object across every render. Returning fresh arrow functions each render — what the base useBoolean does — fails this version.
  • No dependencies allowed. The setters must stay stable even when value changes, so they cannot close over value. Use a functional updater for toggle so it reads the latest value without depending on it.
  • Behavior is unchanged from useBoolean. setTrue always lands on true, setFalse on false, toggle flips. Calling a setter that matches the current value is a no-op.
  • value is not stable, and should not be. Only the three functions are memoized; value is a primitive that changes by design.
Loading editor…