React useMemo Hook

In the last lesson you learned about React Forwarding Refs, so you can reach an element inside a child. This lesson is about speed: how useMemo stops a slow calculation from re-running on every render.

🤔 Why do we need useMemo?

Here’s the pain. A component re-renders for many reasons, and on each render your expensive calculation runs all over again for no reason.

  • You have a big list and you compute something slow from it, like a sorted or filtered version.
  • The user types in a search box, which updates state, which re-renders the component.
  • That heavy calculation runs again, even though the list it depends on never changed.
  • The page starts to feel slow, because work is being repeated needlessly.

So useMemo is about caching. It says “remember this result, and only redo it when the things it depends on change”. The rest of the time, just hand back the saved value.

🐢 The problem in code

Let’s see the waste first. Here a slowSum runs on every render, even when we only toggle an unrelated piece of state.

import { useState } from "react";
function expensiveSum(numbers) {
console.log("Running expensiveSum..."); // shows how often it runs
let total = 0;
for (let i = 0; i < numbers.length; i++) {
total += numbers[i];
}
return total;
}
function Dashboard({ numbers }) {
const [dark, setDark] = useState(false);
// ❌ runs on EVERY render, even when only "dark" changes
const total = expensiveSum(numbers);
return (
<div>
<p>Total: {total}</p>
<button onClick={() => setDark(!dark)}>Toggle theme</button>
</div>
);
}

Look at what happens when you just click the toggle.

  • Clicking “Toggle theme” updates dark, which re-renders Dashboard.
  • On that render, expensiveSum(numbers) runs again from scratch.
  • But numbers did not change at all, so the answer is exactly the same.
  • We paid for the calculation again for nothing.

Output

Running expensiveSum...
(user clicks Toggle theme)
Running expensiveSum...
(user clicks Toggle theme again)
Running expensiveSum...

🧩 The fix: wrap it in useMemo

Now we cache the result. We wrap the calculation in useMemo and tell it what the result depends on, so it only re-runs when those dependencies change.

import { useState, useMemo } from "react";
function Dashboard({ numbers }) {
const [dark, setDark] = useState(false);
// ✅ only re-runs when "numbers" changes, not when "dark" changes
const total = useMemo(() => expensiveSum(numbers), [numbers]);
return (
<div>
<p>Total: {total}</p>
<button onClick={() => setDark(!dark)}>Toggle theme</button>
</div>
);
}

Read the useMemo line slowly, because its shape matters.

  • The first argument is a function that does the work and returns the value.
  • The second argument is the dependency array, [numbers], the list of things the result depends on.
  • React runs the function and saves the result. On the next render, if numbers is the same, it skips the work and returns the saved value.
  • Only when numbers changes does React run the function again and save the new result.

So now toggling the theme re-renders the component, but numbers is unchanged, so expensiveSum does not run again.

Output

Running expensiveSum...
(user clicks Toggle theme - expensiveSum does NOT run)
(user clicks Toggle theme - expensiveSum does NOT run)

🧠 How the dependency array works

The dependency array is the brain of useMemo. It decides when to recalculate and when to reuse.

  • React compares each item in the array to its value from the last render.
  • If every item is the same, it reuses the saved result and skips the function.
  • If any item changed, it runs the function again and saves the fresh result.
  • An empty array [] means “never recalculate after the first time”.
  • So put everything the calculation uses inside the array, or you might get a stale value back.

List all the inputs

Whatever your calculation reads, props or state, must go in the dependency array. Forget one, and useMemo can return an old result that no longer matches the data. This is the most common useMemo bug.

🔗 The other use: stable references

useMemo isn’t only for slow math. It also keeps the same object or array between renders, which matters when something compares by reference.

import { useMemo } from "react";
function Profile({ userId }) {
// ✅ same object identity unless userId changes
const settings = useMemo(() => ({ id: userId, theme: "dark" }), [userId]);
// useEffect that depends on "settings" won't fire on every render now
return <ChildThatTakesSettings settings={settings} />;
}

Here’s why this helps.

  • In JavaScript, {} makes a brand new object every time, even if the contents look the same.
  • So a plain const settings = { ... } is a different object on each render.
  • If you pass it to a memoized child or list it in a useEffect, React thinks it changed every time.
  • useMemo returns the same object until userId changes, so those checks stay quiet.

So useMemo has two jobs: skip expensive work, and keep a value’s identity stable so reference checks don’t fire needlessly.

⚠️ Common Mistakes

The biggest trap is using useMemo everywhere. It is not free, so wrapping trivial work just adds cost.

// ❌ pointless - this calculation is already instant
const doubled = useMemo(() => count * 2, [count]);
// ✅ just compute it directly, no memo needed
const doubled = count * 2;

Keep these in mind so useMemo actually helps.

  • Don’t memoize cheap work. count * 2 or joining two strings is faster than the memo bookkeeping itself.
  • Don’t leave out dependencies. A missing item gives you a stale, wrong value.
  • Don’t put side effects inside it. The function should only calculate and return a value, never fetch data or change state. That’s useEffect’s job.

✅ Best Practices

A few habits tell you when useMemo is worth it.

  • Use it for genuinely heavy work, like sorting or filtering a large list, or a costly transform.
  • Use it to keep object or array references stable when you pass them to memoized children or to effects.
  • Always include every value the calculation reads in the dependency array.
  • When in doubt, measure first. If a calculation is already fast, plain code is clearer and just as good.

Don't optimize too early

Most calculations are fast enough without useMemo. Adding it everywhere makes code harder to read for no real gain. Reach for it when you have a slow calculation or a real reference-stability need, not by default.

🧩 What You’ve Learned

  • ✅ Without useMemo, an expensive calculation re-runs on every render, even unrelated ones
  • useMemo(fn, deps) runs fn, saves the result, and reuses it until something in deps changes
  • ✅ The dependency array must list every prop or state the calculation reads, or you get stale values
  • ✅ An empty [] means calculate once and never again
  • useMemo also keeps object and array references stable so reference checks don’t fire each render
  • ✅ Skip it for cheap work, and never put side effects inside it

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What does useMemo do?

    Why: useMemo remembers the result of a calculation. It only recomputes when something in the dependency array changes, otherwise it returns the saved value.

  2. 2

    What is the second argument to useMemo?

    Why: The second argument is the dependency array. React recalculates only when one of those values changes, and reuses the saved result otherwise.

  3. 3

    When should you NOT use useMemo?

    Why: useMemo has its own small cost. For trivial work like count * 2, plain code is faster and clearer. Memoize only genuinely expensive work or for reference stability.

  4. 4

    What happens if you forget to list a value the calculation uses in the dependency array?

    Why: useMemo only recalculates when a listed dependency changes. If you leave one out, it can keep returning an old value even after that input changed.

🚀 What’s Next?

Now you can stop React from redoing slow calculations on every render. But what about functions? A function is recreated on each render too, and that can cause its own problems. Next you’ll learn the hook that caches a function instead of a value.

React useCallback Hook

Share & Connect