Bitlyst

React memo vs useMemo vs useCallback — When to Use Which

Three React APIs that all say 'memoize' — but they do different things. Clear explanations, examples, and a cheatsheet.

3 min read#react#performance#hooks#memoization#frontend

React memo vs useMemo vs useCallback

All three are about skipping unnecessary work. But they work differently.


🧠 The Core Idea

React re-renders a component whenever its parent re-renders — even if nothing changed. Memoization lets you say:

"Only redo this if the inputs actually changed."


1️⃣ React.memo — Skip re-rendering a component

Wraps a component. Skips re-render if props didn't change.

code
const Button = React.memo(({ onClick, label }: { onClick: () => void; label: string }) => {
  console.log("Button rendered");
  return <button onClick={onClick}>{label}</button>;
});

Without memo: Button re-renders every time the parent does.
With memo: Button skips re-render if onClick and label are the same.


2️⃣ useMemo — Cache a computed value

Memoizes the result of an expensive calculation.

code
const total = useMemo(() => {
  return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
  • Runs only when items changes.
  • Returns the cached value on other renders.

❌ Without useMemo: recalculates on every render.
✅ With useMemo: recalculates only when items changes.


3️⃣ useCallback — Cache a function

Memoizes a function reference so it stays stable across renders.

code
const handleClick = useCallback(() => {
  console.log("clicked", id);
}, [id]);

Without useCallback, a new function is created every render — which breaks React.memo on child components.


🔗 How They Work Together

React.memo compares props with ===. Functions and objects always fail === unless their reference is stable.

code
// ❌ Button re-renders every time — new function each render
<Button onClick={() => doSomething(id)} label="Click" />

// ✅ Button skips re-render — stable reference
const handleClick = useCallback(() => doSomething(id), [id]);
<Button onClick={handleClick} label="Click" />
Parent re-rendersProps compared (===)
Same referencememo skips re-render
New referencememo re-renders child

⚠️ When NOT to Use Them

Memoization has a cost — it takes memory and adds complexity. Don't use it by default.

SituationUse memoization?
Cheap calculation❌ Not worth it
Component renders rarely❌ Skip it
Heavy computation (sort, filter on large arrays)✅ Yes
Passing callbacks to memoized children✅ Yes
Passing objects/arrays as props to memoized children✅ Yes

🧪 Quick Example: All Three Together

code
const ProductList = React.memo(({ items, onSelect }: Props) => {
  // useMemo: only recalculate when items change
  const total = useMemo(
    () => items.reduce((s, i) => s + i.price, 0),
    [items]
  );

  return (
    <div>
      <p>Total: ${total}</p>
      {items.map((item) => (
        <ProductCard key={item.id} item={item} onSelect={onSelect} />
      ))}
    </div>
  );
});

function App() {
  const [cart, setCart] = useState([]);

  // useCallback: stable reference so ProductList doesn't re-render
  const handleSelect = useCallback(
    (item) => setCart((prev) => [...prev, item]),
    []
  );

  return <ProductList items={products} onSelect={handleSelect} />;
}

✅ Summary

APIWrapsSkips
React.memoComponentRe-render when props unchanged
useMemoComputed valueRecalculation when deps unchanged
useCallbackFunctionRecreation when deps unchanged

✨ Final Takeaway

React.memo stops a component from re-rendering. useMemo stops a value from recalculating. useCallback stops a function from being recreated. Use them together — memo needs useCallback to actually work.

You made it to the end!

Did this help? Leave a reaction — it takes one second.

 

Got feedback? 💬

Typo, suggestion, question — I read every message.

Mohsen Fallahnejad
Mohsen Fallahnejad

Writing bite-sized JS, React & Next.js tips

Get new posts in your inbox

No spam. Unsubscribe any time.