Part 19: useCallback Hook: Function Memoization & Optimization
While useMemo caches the result of a calculation, useCallback caches a function definition between renders to maintain referential equality.
1. The Referential Equality Problem
In JavaScript, functions are objects. Every time a component re-renders, any function declared inside it is recreated with a new memory address:
// Render 1:
const fn1 = () => {};
// Render 2:
const fn2 = () => {};
fn1 === fn2; // false! Different memory references!
If you pass this function as a prop to an optimized child component wrapped in React.memo, the child will re-render anyway because it sees a "new" prop!
2. Solving It with useCallback
import { useState, useCallback, memo } from 'react';
// Memoized child component: only re-renders if props change
const DeleteButton = memo(function DeleteButton({ onDelete, id }) {
console.log(`Rendering DeleteButton for item ${id}`);
return <button onClick={() => onDelete(id)}>Delete</button>;
});
export default function ItemManager() {
const [items, setItems] = useState(['Item A', 'Item B', 'Item C']);
const [counter, setCounter] = useState(0);
// handleDelete maintains the same function reference across renders
const handleDelete = useCallback((id) => {
setItems(prev => prev.filter((_, idx) => idx !== id));
}, []);
return (
<div>
<button onClick={() => setCounter(c => c + 1)}>
Rerender Parent (Counter: {counter})
</button>
<ul>
{items.map((item, idx) => (
<li key={idx}>
{item} <DeleteButton onDelete={handleDelete} id={idx} />
</li>
))}
</ul>
</div>
);
}
Key Takeaways
useCallbackmemoizes the function instance itself.- It is primarily useful when passing callbacks to child components optimized with
React.memo. - Always declare all variables used inside the callback in its dependency array.
In Part 20, we will eliminate prop drilling using the useContext Hook!

