Hooks
useMemo
Master the useMemo Hook, covering calculation caching, reference equality for objects, dependency tracking, and optimization guidelines.
1. Learning Objectives
In this lesson, you will learn how to optimize rendering performance by caching expensive calculations. By the end of this topic, you will be able to:
- Identify expensive calculations that require memoization.
- Apply the
useMemoHook to cache calculation outputs between renders. - Explain how
useMemoresolves reference equality issues for objects. - Define correct dependency arrays to trigger recalculations.
- Differentiate between helpful memoization and unnecessary overhead.
2. Overview
The useMemo Hook is a React optimization tool that caches the result of a calculation between renders. By providing a dependency array, you tell React to skip running the calculation function on re-renders unless one of the listed dependency values has changed.
3. Why This Topic Matters
Memoization prevents performance bottlenecks in complex applications:
- Expensive Logic: Running complex calculations (like filtering large lists, sorting rows, or processing charts) on every render stutters the user interface.
- Reference Equality: In JavaScript, objects and arrays are compared by reference, not value. Creating a new object during rendering (e.g.
const options = { active: true }) triggers downstream re-renders because the reference changes on every render.useMemokeeps the reference stable.
4. Real-World Analogy
Think of useMemo like **a math student writing answers on a scratchpad**:
- Without Memoization: Recalculating the answer to a complex equation (like `583 * 291`) every time someone asks for it, starting the math from scratch.
- With Memoization: Calculating the equation once, writing the answer on a scratchpad, and instantly reading the cached answer whenever someone asks again. You only redo the math if the input numbers change (e.g., if they ask for `584 * 291` instead).
5. Core Concepts
Below is a comparison of direct calculations and memoized calculations:
| Feature | Direct Calculation | useMemo Caching |
|---|---|---|
| Execution frequency | Runs on *every* component render cycle. | Runs on mount and only when dependencies update. |
| Object references | Creates a new reference on every render, triggering downstream updates. | Keeps the object reference stable across renders. |
| Use Cases | Cheap calculations (string formatting, basic math). | Expensive computations (sorting arrays, object prop mappings). |
6. Syntax & API Reference
This example shows how to use `useMemo` to filter a list:
7. Visual Diagram
This diagram displays how `useMemo` checks dependencies to determine whether to return cached results or recalculate:
8. Live Example — Full Working Code
Below is a complete HTML page demonstrating performance optimization for list filters:
What just happened? Opening the console shows that "Processing expensive filter calculation..." is logged when clicking "Toggle Evens", but is skipped when clicking "Toggle Theme". This proves useMemo successfully prevents the array filtering logic from re-running on unrelated state updates.
9. Interactive Playground
Try It Yourself Challenges:
- Omit the dependency array or replace it with a new state variable, and verify how the logs track re-renders in the console.
- Add a search filter text input, and add the search string as a dependency to
useMemoto recalculate the filtered list dynamically.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Memoizing cheap calculations | The useMemo Hook has overhead (dependency tracking, cache comparisons, closure creations). Using it for simple operations (like string concatenation or basic addition) actually slows down the application. |
const value = useMemo(() => name.toUpperCase(), [name]); |
const value = name.toUpperCase(); (evaluate directly) |
| Missing dependencies inside the array | If you read variables inside the memoization function but omit them from the dependency array, the Hook will continue to return the cached value using the old state, causing stale data bugs. | const value = useMemo(() => filter(arr, query), []); |
const value = useMemo(() => filter(arr, query), [arr, query]); |
11. Best Practices
- Profile before optimizing: Only use `useMemo` when you notice input latency or slow rendering in a component. Avoid premature optimization.
- Include all dependencies: Ensure any state, props, or variables read inside `useMemo` are listed in its dependency array.
- Use for reference stability: Use `useMemo` to keep object and array references stable when passing them as props to optimized child components (like components wrapped in `React.memo`).
- Never rely on useMemo for semantic logic: React can clear the cache of memoized values at any time to free up memory. Ensure your code works correctly even if values are recalculated.
12. Browser Compatibility/Requirements
The useMemo Hook is supported in all browsers that run React.
13. Interview Questions
Q1: Explain reference equality in JavaScript, and how `useMemo` helps optimize rendering performance when passing objects as props.
Answer: In JavaScript, objects and arrays are compared by reference, not value. Declaring an object directly in a component (e.g. `const config = { active: true }`) creates a new reference on every render, even if the properties are identical. If this object is passed as a prop to a child component, React treats it as a new prop, triggering a re-render. Wrapping the object in `useMemo` keeps the object reference stable across renders, avoiding unnecessary child re-renders.
Q2: Should you wrap every calculation in `useMemo`? Discuss the tradeoffs and overhead involved.
Answer: No. `useMemo` has performance overhead: it must store the previous dependencies and return value in memory, and run a comparison loop on every render. For cheap calculations (like basic string manipulations or small math operations), the cost of this overhead is higher than the cost of recalculating the value. `useMemo` should only be used for expensive computations or to keep object references stable.
14. Debugging Exercise
Identify the bug in this component that causes it to display stale calculation results:
Diagnosis: The dependency array is missing taxRate. If the parent component passes a new taxRate value, the Hook will ignore the change and return the cached value computed with the old tax rate, rendering incorrect data.
Fix: Add taxRate to the dependency array:
15. Practice Exercises
Exercise 1: Create a statistical calculations dashboard
Build a component named DataStats that accepts an array of numbers. Use useMemo to compute statistical metrics like average, median, and standard deviation, preventing these calculations from running on unrelated theme updates.
16. Scenario-Based Challenge
The Virtualized Data Table Row Rendering Bottleneck:
You are designing a grid table containing thousands of rows. Each row displays calculated metrics that depend on a global filter. Describe how you would use useMemo and components structures (like React.memo) to prevent all rows from re-rendering when the user interacts with elements outside the table.
17. Quick Quiz
Q1: What does the useMemo Hook cache between renders?
A) The component function reference itself.
B) The return value of the calculation function.
C) The virtual DOM element objects tree.
Answer: B — useMemo caches the returned value of a calculation function, skipping recalculation unless dependencies change.
18. Summary & Key Takeaways
- The
useMemoHook caches the result of a calculation between renders. - Use
useMemoto optimize expensive calculations and preserve object reference equality. - Always list all variables read inside the memoized function in the dependency array.
- Avoid wrapping cheap calculations in
useMemo, as the overhead can outweigh the benefits.
19. Cheat Sheet
| useMemo Syntax | Purpose |
|---|---|
const x = useMemo(() => fn(), [dep]) |
Caches calculation return values, updating only when dependency parameters change. |
const obj = useMemo(() => ({ k: v }), [v]) |
Keeps the object reference stable across renders. |
const arr = useMemo(() => [...list], [list]) |
Keeps the array reference stable across renders. |