useMemo vs useCallback
useMemo vs useCallback
useMemo and useCallback are two React Hooks that share a nearly identical purpose: they utilize memoization to optimize performance by caching values between renders.
The difference between them lies entirely in what they return.
The Core Difference
useMemocaches the result of calling a function.useCallbackcaches the function itself.
Under the hood, useCallback(fn, deps) is just syntactic sugar for useMemo(() => fn, deps).
When to use useMemo
Use useMemo when you need to cache a value (an array, an object, a number, a string, or JSX).
Scenario 1: Expensive Calculations
// The sorting math only runs when 'users' changes.
const sortedUsers = useMemo(() => {
return users.sort((a, b) => a.score - b.score);
}, [users]);
Scenario 2: Stabilizing Object/Array References
(Often needed when passing objects down to a React.memo wrapped child component).
// The object reference stays exactly the same across renders.
const chartConfig = useMemo(() => {
return { colors: ['red', 'blue'], type: 'bar' };
}, []);
return <MemoizedChart config={chartConfig} />;
When to use useCallback
Use useCallback specifically when you need to cache a function definition.
This is almost exclusively used when you are passing an event handler down as a prop to a highly optimized child component (wrapped in React.memo), or when a function is used as a dependency inside a useEffect.
function Parent() {
const [text, setText] = useState('');
// The function reference stays exactly the same across renders.
// If we didn't use useCallback, MemoizedButton would re-render every time
// the user types in the input field!
const handleSubmit = useCallback(() => {
post('/api', { data: 'hello' });
}, []);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<MemoizedButton onClick={handleSubmit} />
</>
);
}
The Anti-Pattern: Premature Optimization
A very common mistake for intermediate React developers is to wrap every single function in useCallback and every single variable in useMemo.
This is a bad practice.
// ❌ USELESS OPTIMIZATION
function SimpleComponent({ items }) {
const handleClick = useCallback(() => console.log("Clicked!"), []);
// A standard HTML button isn't wrapped in React.memo.
// It doesn't care if the function reference changes.
// We just paid the performance cost of useCallback for absolutely no benefit.
return <button onClick={handleClick}>Click me</button>;
}
Memoization costs memory and CPU time (React has to run the dependency array checks). Only use these hooks if you are solving a specific performance problem, protecting a React.memo child, or preventing an infinite loop in a useEffect.
Interview Questions
Q: If useMemo caches values and useCallback caches functions, can’t I just use useMemo to cache a function?
A: Yes, you can. useCallback(fn, deps) is functionally identical to writing useMemo(() => fn, deps). useCallback was added purely as syntactic sugar for that exact pattern, because caching functions is so common in React.