useMemo vs useCallback

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 2 min

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

  • useMemo caches the result of calling a function.
  • useCallback caches 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.