useCallback Hook
In this page:
Why Function References Matter
Every time a component renders, any function defined inside it is recreated as a brand-new function, even if its code looks identical. This matters when that function is passed as a prop to a child wrapped in React.memo, because a new function reference makes the child think its props changed.
Note: Function identity ('is this the same function as before') is what React.memo checks -- not just whether the code inside looks the same.
Warning: Passing an inline arrow function directly as a prop recreates it every render, which can defeat a child's React.memo optimization.
Example: Why Function References Matter
<!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 Parent() {
const [count, setCount] = React.useState(0);
function handleClick() { console.log("Clicked"); } // recreated every render
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<Parent />);
</script>
</body>
</html>
Memoizing a Function with useCallback
useCallback(fn, dependencies) returns the same function reference across renders, as long as the listed dependencies haven't changed. This lets you pass a genuinely stable function reference down to memoized child components.
Note: Pair useCallback with React.memo on the receiving child component -- using one without the other provides no real benefit.
Warning: If you forget to list a variable the callback uses in its dependency array, the callback will use a stale, outdated value of that variable.
Example: Memoizing a Function with useCallback
<!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 Button({ onClick, label }) {
return <button onClick={onClick}>{label}</button>;
}
const MemoButton = React.memo(Button);
function Parent() {
const [count, setCount] = React.useState(0);
const handleClick = React.useCallback(() => {
console.log("Stable callback");
}, []);
return <div><p>{count}</p><MemoButton onClick={handleClick} label="Click" /><button onClick={() => setCount(count + 1)}>+1</button></div>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<Parent />);
</script>
</body>
</html>
useCallback vs useMemo
useCallback and useMemo solve the same underlying problem in slightly different ways: useMemo caches a calculated VALUE, while useCallback caches a FUNCTION itself. In fact, useCallback(fn, deps) behaves the same as useMemo(() => fn, deps).
Note: Use useMemo when you want to cache a computed result; use useCallback specifically when you want to cache a function reference.
Warning: Confusing the two syntaxes -- like wrapping a function in useMemo without returning it from an inner function -- is a common typo-level mistake.
Example: useCallback vs useMemo
<!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 Demo() {
const [n, setN] = React.useState(1);
const doubled = React.useMemo(() => n * 2, [n]); // caches a VALUE
const logDoubled = React.useCallback(() => console.log(doubled), [doubled]); // caches a FUNCTION
return <button onClick={logDoubled}>Doubled: {doubled}</button>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<Demo />);
</script>
</body>
</html>
- Wrapping every function in useCallback by default, even ones with no real performance need for it.
- Forgetting to include values the callback closes over in its dependency array, causing it to use stale data.
- Expecting useCallback alone to prevent a child component from re-rendering -- it must be paired with React.memo on the child to have that effect.
- useCallback memoizes a function so the same function reference is reused between renders.
- It's useful when passing callbacks to child components wrapped in React.memo.
- Without a stable reference, a new function is created every render, defeating memoization in children.
- Like useMemo, it should be used for genuine performance needs, not by default everywhere.
Available since React 16.8, when Hooks were introduced.
Chapter Quiz — Complete all 12 topics to unlock
0/12 topics done
Complete these topics first: