useToggleLoading saved progress…

useToggle

Build a custom React hook that owns a single boolean and hands back a function to flip it. Toggles are everywhere — a dark-mode switch, a "show more" panel, a modal that's open or closed, a checkbox. useToggle(initialValue) returns a tuple [value, toggle]: read value to render, call toggle() to invert it. The interesting part isn't the boolean; it's wiring toggle so it flips correctly even when called twice in a row, accepts an explicit target, survives being attached straight to an onClick, and keeps the same function identity across renders.

Signature

function useToggle(initialValue?: boolean): [boolean, (next?: boolean) => void];

initialValue defaults to false. Call toggle() with no argument to flip; call toggle(true) or toggle(false) to set it explicitly. If toggle is handed a non-boolean (like a click event), it ignores the argument and flips.

Examples

function Panel() {
  const [open, toggle] = useToggle(false);

  return (
    <div>
      <button onClick={toggle}>{open ? 'Hide' : 'Show'}</button>
      {open && <p>Now you can see me.</p>}
    </div>
  );
}
// starts hidden; clicking the button flips open ↔ closed each time
// no-arg flips; a boolean sets explicitly; a non-boolean still flips
const [value, toggle] = useToggle(false);
toggle();      // value === true   (flipped)
toggle(false); // value === false  (set explicitly)
toggle(true);  // value === true   (set explicitly)
toggle(true);  // value === true   (idempotent — set, not flip)
toggle({ type: 'click' }); // value === false (non-boolean → flip)

Notes

  • No argument flips; a boolean sets. toggle() inverts the current value, but toggle(true) / toggle(false) force a specific value regardless of what it was.
  • A non-boolean argument must still flip. When toggle is passed straight to onClick, React calls it with an event object. Guard with typeof next === 'boolean' so the truthy event does not get read as true.
  • toggle must keep a stable identity. It should be the same function reference on every render, not a fresh one each time, so it can be a safe dependency and not break memoized children.
  • Two flips in one event must cancel out. Calling toggle() twice in a single update should land back on the original value — this is what separates a correct implementation from the stale-closure trap.
Loading editor…