React useMemo Hook
Table of Contents + −
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-rendersDashboard. - On that render,
expensiveSum(numbers)runs again from scratch. - But
numbersdid 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
numbersis the same, it skips the work and returns the saved value. - Only when
numberschanges 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. useMemoreturns the same object untiluserIdchanges, 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 instantconst doubled = useMemo(() => count * 2, [count]);
// ✅ just compute it directly, no memo neededconst doubled = count * 2;Keep these in mind so useMemo actually helps.
- Don’t memoize cheap work.
count * 2or 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)runsfn, saves the result, and reuses it until something indepschanges - ✅ 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 - ✅
useMemoalso 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
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
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
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
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.