Building a useToggle Hook
In this page:
Building a Minimal useToggle
A toggle is one of the simplest and most common pieces of UI state: a sidebar is either open or closed, a checkbox is either checked or not. useToggle wraps this pattern into a two-item return: the current boolean value and a function to flip it.
Note: Give the hook a default value parameter (e.g. useToggle(false)) so callers can choose the starting state.
Warning: Don't name the hook's return value something generic like value when using it — destructure it with a meaningful name like isOpen at the call site.
Example: Building a Minimal useToggle
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function useToggle(initial = false) {
const [value, setValue] = React.useState(initial);
const toggle = () => setValue(v => !v);
return [value, toggle];
}
function App() {
const [isOpen, toggleOpen] = useToggle(false);
return (
<div>
<button onClick={toggleOpen}>Toggle Menu</button>
{isOpen && <p>Menu is open!</p>}
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Why the Functional Updater Matters
Writing setValue(!value) reads value from the render where the function was created, which can be stale if multiple toggles happen quickly (like a fast double-click). Writing setValue(v => !v) instead always flips whatever the actual latest state is, avoiding that bug.
Note: As a habit, always use the functional updater form inside custom hooks, since you don't control how often or how fast the caller might invoke them.
Warning: Two rapid calls to setValue(!value) in the same event handler can both compute from the same stale value and cancel each other out instead of toggling twice.
Example: Why the Functional Updater Matters
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function useToggle(initial = false) {
const [value, setValue] = React.useState(initial);
const toggle = () => setValue(v => !v);
return [value, toggle];
}
function App() {
const [isOn, toggle] = useToggle(false);
const toggleTwice = () => { toggle(); toggle(); };
return <div><p>State: {isOn ? "ON" : "OFF"}</p><button onClick={toggleTwice}>Toggle Twice (stays same)</button></div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Extending useToggle with Explicit Setters
Sometimes a caller needs to force the value to true or false directly, not just flip it — for example, closing a modal explicitly when clicking outside it. Returning an object with toggle, setTrue, and setFalse gives callers that extra control.
Note: Returning an object (instead of an array) is a good choice here since there are three related functions with distinct purposes.
Warning: If you add setTrue/setFalse, make sure they also use setValue(true)/setValue(false) directly rather than relying on toggle() twice, to keep behavior predictable.
Example: Extending useToggle with Explicit Setters
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function useToggle(initial = false) {
const [value, setValue] = React.useState(initial);
return { value, toggle: () => setValue(v => !v), setTrue: () => setValue(true), setFalse: () => setValue(false) };
}
function App() {
const modal = useToggle(false);
return (
<div>
<button onClick={modal.setTrue}>Open Modal</button>
{modal.value && <div><p>Modal content</p><button onClick={modal.setFalse}>Close</button></div>}
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Rebuilding the same 'const [on, setOn] = useState(false); const toggle = () => setOn(!on);' logic in every component instead of extracting it.
- Using setOn(!on) instead of the functional form setOn(o => !o), which can read stale state in rapid clicks.
- Forgetting to also expose direct setTrue/setFalse setters when the caller needs more control than just toggling.
- useToggle is a small custom hook that manages a single boolean value with a toggle function.
- It's a simplification built on top of useState, useful for menus, modals, and switches.
- Using the functional state updater form avoids bugs from stale closures.
- It's often extended to also return explicit setTrue/setFalse functions alongside toggle.
Available since React 16.8 (Hooks introduction) — no special browser requirements.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: