Composing Custom Hooks
In this page:
function useComposed() {
const first = useFirstHook();
const second = useSecondHook(first);
return { first, second };
}
Combining Multiple Hooks into One
A custom hook isn't limited to calling only built-in hooks like useState — it can also call other custom hooks. This lets you build a more powerful hook out of smaller, focused pieces, the same way components are built out of smaller components.
Example: Combining Multiple Hooks into One
<!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) {
const [value, setValue] = React.useState(initial);
return [value, () => setValue(v => !v)];
}
function useCounter(start) {
const [count, setCount] = React.useState(start);
return [count, () => setCount(c => c + 1)];
}
function useComposed() {
const [isOn, toggle] = useToggle(false);
const [count, increment] = useCounter(0);
return { isOn, toggle, count, increment };
}
function App() {
const c = useComposed();
return <button onClick={() => { c.toggle(); c.increment(); }}>{c.isOn ? "ON" : "OFF"} ({c.count})</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
A Real Example: useOnlineStatus + useLocalStorage
Composition shines when you combine hooks that each solve a different problem. Here, a useLastSeen hook composes an online-status hook with a localStorage hook, so it can record and persist the last time the user was online.
Example: A Real Example: useOnlineStatus + useLocalStorage
<!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 useLocalValue(key, initial) {
const [value, setValue] = React.useState(initial);
React.useEffect(() => { localStorage.setItem(key, value); }, [key, value]);
return [value, setValue];
}
function useLastSeen() {
const [lastSeen, setLastSeen] = useLocalValue("lastSeen", "never");
return [lastSeen, () => setLastSeen(new Date().toLocaleTimeString())];
}
function App() {
const [lastSeen, markSeen] = useLastSeen();
return <div><p>Last seen: {lastSeen}</p><button onClick={markSeen}>Mark as seen now</button></div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Keeping Composed Hooks Readable
As you compose more hooks together, naming and structure matter more. Destructuring each inner hook's result with clear names, and returning a well-named object from the outer hook, keeps the composed hook easy to understand at a glance.
Example: Keeping Composed Hooks Readable
<!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) {
const [value, setValue] = React.useState(initial);
return { value, toggle: () => setValue(v => !v) };
}
function useCounter(start) {
const [count, setCount] = React.useState(start);
return { count, increment: () => setCount(c => c + 1) };
}
function useFeature() {
return { visibility: useToggle(true), clicks: useCounter(0) };
}
function App() {
const { visibility, clicks } = useFeature();
return <div>{visibility.value && <p>Clicks: {clicks.count}</p>}<button onClick={clicks.increment}>Click</button><button onClick={visibility.toggle}>Toggle</button></div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Building one giant hook that does everything instead of composing several small, focused hooks together.
- Not realizing that a hook can call other custom hooks — beginners sometimes think only built-in hooks can be combined.
- Creating circular dependencies between hooks, where two custom hooks each try to depend on the other's output.
- Hook composition means building a custom hook out of other hooks, including other custom hooks.
- It lets you build complex, reusable behavior (like useAuthenticatedFetch) from small, single-purpose hooks.
- Each composed hook still follows the same rules of hooks (top-level calls, no conditionals).
- This mirrors how small components compose into bigger components — hooks compose the same way for logic.
Available since React 16.8 (Hooks introduction) — composition is a JavaScript pattern, not a browser feature.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: