React useToggle Hook
Boolean state shows up everywhere: modals, drawers, accordions, password visibility, dark mode. The useToggle hook collapses three lines of `useState` plus a setter into one call. This snippet covers the minimal toggle, a value-aware variant that lets callers pin the state to a specific boolean, and an enum-aware version that cycles through any number of states (light / dark / system).
631 views
16
function useToggle(initial = false) {
const [on, setOn] = useState(initial);
const toggle = () => setOn((v) => !v);
return [on, toggle];
}
function useState(v) { return [v, () => {}]; }
const [open, toggleOpen] = useToggle();
console.log('initial open:', open);The hook returns the current boolean alongside a setter that flips it. Using the functional form setOn((v) => !v) is essential: a plain setOn(!on) captures on from render scope and breaks if two toggles fire in the same tick. The signature [value, toggle] mirrors useState so destructuring stays familiar. This is the version you want for password show / hide, drawer open / close, and any one-shot boolean.
function useToggleSet(initial = false) {
const [on, setOn] = useState(initial);
const toggle = (next) => {
setOn((v) => (typeof next === 'boolean' ? next : !v));
};
return [on, toggle];
}
const [visible, setVisible] = useToggleSet();
setVisible(true);
setVisible();
console.log('initial visible:', visible);The minimal version cannot pin the state to a specific value, which matters for handlers like onMouseEnter (always show) and onMouseLeave (always hide). Accepting an optional explicit boolean keeps both behaviours in one helper. The check typeof next === 'boolean' avoids treating React synthetic event objects as truthy values when callers pass the toggle directly to onClick. This shape is what most production codebases land on after a couple of refactors.
function useCycle(values) {
const [index, setIndex] = useState(0);
const cycle = () => setIndex((i) => (i + 1) % values.length);
return [values[index], cycle];
}
const [theme, nextTheme] = useCycle(['light', 'dark', 'system']);
console.log('current theme:', theme);Sometimes the state has more than two values: theme settings (light / dark / system), tab orders, or sort directions (none / asc / desc). useCycle keeps an index instead of a boolean and wraps with modulo so the cycle never overflows. The current value (not the index) is returned so call sites read like theme === 'dark'. This is conceptually a generalisation of useToggle and a frequent pattern in design-system primitives.
