useMemo
useMemo in React explains caching expensive calculations, dependency arrays, referential stability, and when memoization helps versus when it adds needless complexity.
useMemo is a React Hook that lets you cache the result of a calculation between renders.
It is primarily useful when a calculation is expensive and does not need to be repeated unless its dependencies change.
The basic pattern is:
const cachedValue = useMemo(() => calculateValue(data),[data]);
A useful mental model is:
Render↓Do we already have a cached resultfor the same dependencies?│├── Yes → Reuse cached result│└── No → Calculate → Store result
useMemo is a performance optimization, not a replacement for correct application logic.
Why useMemo Exists
React components can render many times.
For example:
function ProductList({ products, searchTerm }) {const filteredProducts = filterProducts(products,searchTerm);return (<ul>{filteredProducts.map(product => (<li key={product.id}>{product.name}</li>))}</ul>);}
Every render calls:
filterProducts(products, searchTerm);
If the calculation is expensive, repeatedly performing it can become costly.
useMemo allows React to reuse the previously calculated result when its dependencies have not changed:
const filteredProducts = useMemo(() => filterProducts(products, searchTerm),[products, searchTerm]);
Importing useMemo
Import it from React:
import { useMemo } from "react";
It can be used with other Hooks:
import {useMemo,useState} from "react";
Basic Syntax
The syntax is:
const value = useMemo(() => calculateValue(),[dependencies]);
For example:
const total = useMemo(() => calculateTotal(items),[items]);
The first argument is a function that calculates the value.
The second argument is the dependency array.
Understanding the Calculation Function
Consider:
const total = useMemo(() => calculateTotal(items),[items]);
The function:
() => calculateTotal(items)
describes how to calculate the value.
React can call that function when it needs to produce a new memoized result.
You do not call the function yourself:
// IncorrectuseMemo(calculateTotal(items), [items]);
Instead:
// CorrectuseMemo(() => calculateTotal(items), [items]);
Understanding Dependencies
Consider:
const filteredProducts = useMemo(() => filterProducts(products, searchTerm),[products, searchTerm]);
The calculation depends on:
productssearchTerm
Therefore both are included in the dependency array.
Mental model:
products changed?ORsearchTerm changed?│▼RecalculateNeither changed?│▼Reuse cached value
React compares dependencies between renders using Object.is semantics.
A Simple Example
import { useMemo } from "react";function Price({ quantity, price }) {const total = useMemo(() => quantity * price,[quantity, price]);return <p>Total: ₹{total}</p>;}
This example demonstrates the API, but the calculation:
quantity * price
is extremely cheap.
Therefore, useMemo is unnecessary here.
A simpler implementation is better:
function Price({ quantity, price }) {const total = quantity * price;return <p>Total: ₹{total}</p>;}
This illustrates an important principle:
Do not add useMemo simply because you can.
useMemo Is for Optimization
useMemo should generally be introduced when there is a meaningful performance reason.
Good candidate:
const visibleProducts = useMemo(() => expensiveFilter(products, searchTerm),[products, searchTerm]);
Poor candidate:
const fullName = useMemo(() => `${firstName} ${lastName}`,[firstName, lastName]);
The second calculation is trivial.
Use:
const fullName = `${firstName} ${lastName}`;
instead.
What Does "Memoize" Mean?
Memoization means remembering the result of a calculation so that it can be reused when the inputs have not changed.
Conceptually:
Input↓Calculation↓Result↓Cache
On a later render:
Same inputs↓Reuse result
When inputs change:
Different inputs↓Calculate again↓Replace cached result
useMemo and Rendering
Consider:
function Dashboard({ products, searchTerm }) {const filteredProducts = useMemo(() => filterProducts(products, searchTerm),[products, searchTerm]);return (<div><ProductList products={filteredProducts} /></div>);}
Suppose the dashboard also has unrelated state:
const [isOpen, setIsOpen] = useState(false);
If isOpen changes:
isOpen changes↓Dashboard renders↓products unchangedsearchTerm unchanged↓useMemo reuses filteredProducts
The component still renders, but the expensive calculation can be skipped.
Important:
useMemo does not prevent the component from rendering.
It only memoizes the calculation's result.
useMemo Does Not Prevent Re-Renders
This is a common misconception.
Incorrect:
useMemo stops my component from re-rendering.
It does not.
For example:
const result = useMemo(() => expensiveCalculation(data),[data]);
The component can still render because:
- Its state changed.
- Its parent rendered.
- Its props changed.
- Context changed.
useMemo only controls whether the memoized calculation needs to run again.
useMemo vs React.memo
These are different tools.
useMemo
Memoizes a calculated value:
const filtered = useMemo(() => filterItems(items),[items]);
React.memo
Memoizes a component's rendering based on props:
const ProductCard = memo(function ProductCard({product}) {return <div>{product.name}</div>;});
Conceptually:
useMemo↓Memoize a valuememo↓Memoize component rendering
They can sometimes be used together, but they solve different problems.
useMemo vs useCallback
useMemo caches a value:
const result = useMemo(() => calculateSomething(data),[data]);
useCallback caches a function reference:
const handleClick = useCallback(() => doSomething(id),[id]);
Mental model:
useMemo→ "Remember this calculated value."useCallback→ "Remember this function reference."
The next lesson covers useCallback in detail.
useMemo and Referential Equality
One important reason to use useMemo is maintaining a stable object or array reference when that stability matters.
Consider:
function Dashboard({ items }) {const options = {sort: "price",limit: 20};return <ProductList items={items} options={options} />;}
A new object is created on every render.
Even though its contents are the same:
Render 1 → options object ARender 2 → options object BRender 3 → options object C
If a child relies on reference equality, this can cause unnecessary work.
You can memoize it:
const options = useMemo(() => ({sort: "price",limit: 20}),[]);
Now React can reuse the same object reference between renders.
However, do not memoize objects merely because they are objects. There should be a real reason for stable identity.
useMemo With Child Components
Suppose:
const ProductList = memo(function ProductList({products}) {return (<ul>{products.map(product => (<li key={product.id}>{product.name}</li>))}</ul>);});
If the parent creates a new array every render:
const visibleProducts = products.filter(...);
the child receives a new array reference.
Even if the contents are the same, the prop reference changes.
Using:
const visibleProducts = useMemo(() => products.filter(...),[products]);
can preserve the same array reference when products has not changed.
This can make memo more effective.
Example: Expensive Filtering
import { useMemo, useState } from "react";function ProductSearch({ products }) {const [searchTerm, setSearchTerm] = useState("");const [isDarkMode, setIsDarkMode] = useState(false);const filteredProducts = useMemo(() => {return products.filter(product =>product.name.toLowerCase().includes(searchTerm.toLowerCase()));}, [products, searchTerm]);return (<div className={isDarkMode ? "dark" : "light"}><inputvalue={searchTerm}onChange={event =>setSearchTerm(event.target.value)}placeholder="Search products"/><buttononClick={() =>setIsDarkMode(previous => !previous)}>Toggle Theme</button><p>Results: {filteredProducts.length}</p><ul>{filteredProducts.map(product => (<li key={product.id}>{product.name}</li>))}</ul></div>);}
When the theme changes:
isDarkMode changes↓Component renders↓products unchangedsearchTerm unchanged↓filteredProducts reused
This is a practical use of memoization when filtering is sufficiently expensive.
Example: Sorting
Sorting a large dataset can also be expensive.
const sortedProducts = useMemo(() => {return [...products].sort((a, b) => a.price - b.price);}, [products]);
Notice:
[...products]
creates a new array before sorting.
Avoid:
products.sort(...);
because sort() mutates the array.
Example: Grouping Data
Suppose a dashboard groups thousands of transactions:
const transactionsByCategory = useMemo(() => {return groupTransactions(transactions);}, [transactions]);
The grouping operation can be reused when unrelated component state changes.
Example: Expensive Calculation
const result = useMemo(() => {return performExpensiveCalculation(dataset,configuration);}, [dataset, configuration]);
If the component re-renders because some unrelated state changes:
unrelated state↓component renders↓dataset unchangedconfiguration unchanged↓cached result reused
Dependency Mistakes
One of the most important rules is to specify the values used by the calculation.
Consider:
const filtered = useMemo(() => filterProducts(products, searchTerm),[products]);
The calculation uses:
productssearchTerm
but only products is listed.
If searchTerm changes, the memoized value may remain stale.
Prefer:
const filtered = useMemo(() => filterProducts(products, searchTerm),[products, searchTerm]);
The dependency array should describe the calculation's reactive inputs.
Empty Dependency Array
You can write:
const value = useMemo(() => expensiveCalculation(),[]);
This means the calculation has no reactive dependencies.
However, be careful.
If the calculation uses values from the component:
const value = useMemo(() => expensiveCalculation(data),[]);
then data is missing from the dependency list.
Do not use [] simply to force a calculation to happen once.
Empty Dependencies and Initial Values
Sometimes developers write:
const config = useMemo(() => createConfig(),[]);
This can be reasonable if the calculation truly has no reactive inputs and the stable reference is useful.
But for a simple constant, this is unnecessary:
const config = {mode: "production"};
Or move a truly static value outside the component:
const config = {mode: "production"};
The simplest correct solution is usually best.
useMemo and Object Dependencies
Consider:
const options = {minPrice,maxPrice};const products = useMemo(() => filterProducts(allProducts, options),[allProducts, options]);
Because options is recreated on every render, its reference changes every time.
That can cause the memoized calculation to run every render.
One solution is to move the object creation into the memo calculation:
const products = useMemo(() => {const options = {minPrice,maxPrice};return filterProducts(allProducts, options);}, [allProducts, minPrice, maxPrice]);
Now the dependencies represent the actual reactive inputs.
useMemo Is Not a Semantic Guarantee
Memoization is an optimization.
Do not write code that is logically dependent on React preserving a memoized value forever.
Your application should remain correct even if the calculation is performed again.
Think:
useMemo↓Potentially skip expensive work
not:
useMemo↓Guarantee permanent storage
React may discard memoized values in situations where it needs to.
Therefore, useMemo should not be used as a substitute for state, refs, or external persistence.
useMemo Is Not State
Incorrect:
const selected = useMemo(() => calculateSelection(),[items]);
and then treating selected as something that can be updated.
useMemo does not provide a setter.
It calculates a value from dependencies.
If the component needs independently changing state:
const [selected, setSelected] = useState(null);
is appropriate.
useMemo Is Not an Effect
Avoid:
const data = useMemo(() => {saveToLocalStorage(value);return value;}, [value]);
This is mixing side effects into a memoized calculation.
Use an effect for synchronization:
useEffect(() => {localStorage.setItem("value", value);}, [value]);
Use useMemo for calculations.
Use useEffect for external synchronization.
useMemo and Pure Calculations
A good memoized calculation should be pure.
For example:
const total = useMemo(() => calculateTotal(items),[items]);
The calculation should:
- Depend on its inputs.
- Return a result.
- Avoid modifying external systems.
- Avoid mutating input data.
- Avoid triggering state updates.
Pure calculations are predictable and easy to memoize.
Avoid Mutating Inputs
Incorrect:
const sortedProducts = useMemo(() => {return products.sort((a, b) => a.price - b.price);}, [products]);
sort() mutates the original array.
Prefer:
const sortedProducts = useMemo(() => {return [...products].sort((a, b) => a.price - b.price);}, [products]);
The original state or prop remains unchanged.
useMemo and Large Lists
Imagine:
const visibleProducts = useMemo(() => products.filter(product => {// expensive logic}),[products, searchTerm, category]);
For a very large list, memoization can reduce repeated filtering when unrelated state changes.
But if the list contains only a few items, the cost of memoization itself may not be worthwhile.
The goal is not:
Use useMemo everywhere
The goal is:
Measure or reason about expensive work↓Memoize when useful
useMemo and Performance Measurement
Before optimizing, identify whether there is an actual performance issue.
Useful approaches include:
- React DevTools Profiler
- Browser performance tools
- Measuring expensive calculations
- Observing render behavior
- Checking real user performance
Do not optimize based only on assumptions.
For example:
const fullName = useMemo(() => `${firstName} ${lastName}`,[firstName, lastName]);
usually adds complexity without solving a meaningful performance problem.
Common useMemo Mistakes
Mistake 1: Memoizing Everything
Avoid:
const name = useMemo(() => `${firstName} ${lastName}`,[firstName, lastName]);
Prefer:
const name = `${firstName} ${lastName}`;
Mistake 2: Using useMemo to Prevent Re-renders
useMemo does not stop the component from rendering.
It memoizes a calculation's result.
Mistake 3: Missing Dependencies
Incorrect:
const result = useMemo(() => calculate(data, filter),[data]);
Correct:
const result = useMemo(() => calculate(data, filter),[data, filter]);
Mistake 4: Using useMemo for Side Effects
Avoid:
useMemo(() => {saveData(data);}, [data]);
Use:
useEffect(() => {saveData(data);}, [data]);
Mistake 5: Mutating Data During Memoization
Avoid:
products.sort(...);
Prefer:
[...products].sort(...);
Mistake 6: Using useMemo as Persistent Storage
Do not rely on:
useMemo(...)
as a permanent storage mechanism.
Use:
useState → stateuseRef → mutable persistent valueexternal storage → durable persistence
When Should You Use useMemo?
Good candidates include:
Expensive calculations
const result = useMemo(() => expensiveCalculation(data),[data]);
Large filtering operations
const filtered = useMemo(() => filterLargeList(items, search),[items, search]);
Expensive sorting
const sorted = useMemo(() => [...items].sort(compareItems),[items]);
Expensive grouping or transformation
const grouped = useMemo(() => groupData(records),[records]);
Stable object or array identity
When a stable reference is actually useful:
const options = useMemo(() => ({pageSize: 20,sortBy: "price"}),[]);
When Should You Not Use useMemo?
Avoid it for trivial calculations:
const total = price * quantity;
const fullName = `${firstName} ${lastName}`;
const isAdult = age >= 18;
Also avoid using it just because:
- A component renders frequently
- You want every value to be cached
- You heard memoization improves performance
- A value is an object
- A calculation is inside a component
The performance trade-off should justify the additional complexity.
A Practical Optimization Example
Suppose a dashboard contains:
const [search, setSearch] = useState("");const [theme, setTheme] = useState("light");
and processes a large dataset:
const visibleReports = generateReports(reports,search);
Every theme change causes the component to render, which can repeat:
generateReports(reports, search);
If report generation is expensive:
const visibleReports = useMemo(() => generateReports(reports, search),[reports, search]);
Now:
Theme changes↓Dashboard renders↓reports unchangedsearch unchanged↓Reuse visibleReports
This is a realistic reason to introduce useMemo.
useMemo and Child Optimization
A common optimization chain is:
useMemo↓Stable calculated value↓memoized child receives same reference↓Child can skip unnecessary render
Example:
const visibleProducts = useMemo(() => filterProducts(products, search),[products, search]);return (<ProductListproducts={visibleProducts}/>);
If ProductList is memoized:
const ProductList = memo(function ProductList({products}) {// ...});
the stable array reference can help it avoid rendering when the list has not logically changed.
However, this optimization only matters if the child is actually a performance bottleneck.
useMemo and Derived Data
Memoized values are still derived data.
For example:
const total = useMemo(() => calculateTotal(items),[items]);
The source of truth is still:
items
The total is derived.
This is different from storing both:
const [items, setItems] = useState([]);const [total, setTotal] = useState(0);
and synchronizing them with effects.
A memoized value can be a useful optimization while keeping one source of truth.
A Useful Optimization Hierarchy
When a calculation is expensive, consider optimization in this order:
1. Is the calculation actually expensive?↓2. Can the calculation be simplified?↓3. Can unnecessary renders be reduced?↓4. Can data structures or algorithms improve?↓5. Would useMemo provide a measurable benefit?
Do not start with useMemo automatically.
Good React performance comes from good component design first.
useMemo and Algorithmic Complexity
Memoization cannot fix an inefficient algorithm.
For example, if:
calculateResults(data);
takes a long time because it performs unnecessary work, first consider improving the algorithm.
For example:
O(n²)↓Can it become O(n)?
An improved algorithm may provide a much larger benefit than caching.
Then useMemo can be considered if repeated calculation still matters.
Advanced Example: Expensive Dashboard Statistics
function Dashboard({ transactions }) {const statistics = useMemo(() => {return {total: calculateTotal(transactions),average: calculateAverage(transactions),categories: groupByCategory(transactions)};}, [transactions]);return (<div><p>Total: {statistics.total}</p><p>Average: {statistics.average}</p><CategorySummarycategories={statistics.categories}/></div>);}
Here multiple calculations depend on the same dataset.
If these operations are genuinely expensive, memoization can prevent them from repeating when unrelated component state changes.
Debugging useMemo
If a memoized calculation runs more often than expected, inspect:
1. Are dependencies changing?
For example:
[items, options]
If options is recreated every render, the memo may invalidate every time.
2. Are arrays or objects recreated?
Check reference identity.
3. Is the calculation actually expensive?
If not, remove the memoization.
4. Is a child still rendering?
Remember that useMemo only memoizes the value. It does not automatically memoize child components.
5. Are you using memoization to hide an architectural problem?
Sometimes the better solution is to move state or split components.
Best Practices
1. Optimize Based on Need
Use useMemo when there is a meaningful performance reason.
2. Keep Calculations Pure
The callback should calculate and return a value.
3. Declare Dependencies Correctly
Include the reactive values used by the calculation.
4. Avoid Trivial Memoization
Do not make simple expressions harder to read.
5. Avoid Mutation
Create new arrays and objects instead of modifying inputs.
6. Remember That useMemo Does Not Prevent Rendering
It only caches a calculation result.
7. Consider Stable References Carefully
Memoization can help when reference identity matters, but do not optimize references without a real reason.
8. Profile Before and After
Verify that the optimization actually improves performance.
Interview questions & answers
Interview preparation
10 questions grouped by difficulty — expand any card for a model answer and follow-up probe.
Beginner
1What is useMemo?
BeginnerModel answer
useMemois a React Hook that caches the result of a calculation between renders until its dependencies change.2What does the dependency array do?
BeginnerModel answer
It tells React which reactive values the calculation depends on.useMemo( → () => calculate(data), → [data] → );3Should every expensive-looking calculation use useMemo?
BeginnerModel answer
No.
Measure or reason about the actual cost and use memoization when it provides a meaningful benefit.
4Is useMemo a guarantee that React will never recalculate the value?
BeginnerModel answer
No.
It is a performance optimization, not a semantic guarantee of permanent storage.
Intermediate
5Why use useMemo?
IntermediateModel answer
It can avoid repeating expensive calculations when their inputs have not changed.
6What is the difference between useMemo and useCallback?
IntermediateModel answer
useMemomemoizes a calculated value.useCallbackmemoizes a function reference.7Can useMemo be used for side effects?
IntermediateModel answer
No. Side effects belong inuseEffector event handlers depending on what triggers them.
Advanced
8Does useMemo prevent component re-renders?
AdvancedModel answer
No.
It only memoizes the calculation's result.
9What is the difference between useMemo and React.memo?
AdvancedModel answer
useMemomemoizes a value inside a component.React.memocan memoize a component so it can skip rendering when its props have not changed.10Can useMemo replace useState?
AdvancedModel answer
No.useMemocalculates a value. It does not provide a state setter or independent state transitions.
Practice Exercises
Exercise 1: Expensive Calculation
Create a component that:
- Receives a large number.
- Performs an intentionally expensive calculation.
- Uses
useMemoto cache the result. - Includes unrelated state to demonstrate when the calculation can be skipped.
Exercise 2: Product Search
Create a product search component that:
- Receives a large product array.
- Filters products by search term.
- Uses
useMemo. - Displays the number of matching products.
Exercise 3: Product Sorting
Create a component that:
- Receives products.
- Allows sorting by price.
- Uses
useMemoto calculate the sorted list. - Does not mutate the original products array.
Exercise 4: Dashboard Statistics
Create a dashboard that calculates:
- Total sales
- Average sale
- Number of transactions
- Sales grouped by category
Use useMemo for the calculations if they are sufficiently expensive.
Exercise 5: Stable Options Object
Create a parent component that passes an options object to a memoized child.
First observe how a newly created object affects the child.
Then use useMemo to provide a stable object reference.
Exercise 6: Remove Unnecessary Memoization
Take code such as:
const fullName = useMemo(() => `${firstName} ${lastName}`,[firstName, lastName]);
Refactor it to:
const fullName = `${firstName} ${lastName}`;
Explain why the second version is preferable.
Mini Project: Optimized Product Dashboard
Build a product dashboard with:
Product listSearchCategory filterPrice sortingTheme toggle
Requirements:
- Store search state with
useState. - Store category state with
useState. - Store sort state with
useState. - Filter products.
- Sort products.
- Use
useMemofor genuinely expensive filtering/sorting. - Toggle theme independently.
- Demonstrate that changing theme does not require recalculating unchanged product results.
Example structure:
function ProductDashboard({ products }) {const [search, setSearch] = useState("");const [category, setCategory] = useState("all");const [sortBy, setSortBy] = useState("price");const [darkMode, setDarkMode] = useState(false);const visibleProducts = useMemo(() => {let result = products;if (search) {result = result.filter(product =>product.name.toLowerCase().includes(search.toLowerCase()));}if (category !== "all") {result = result.filter(product => product.category === category);}return [...result].sort((a, b) => {if (sortBy === "price") {return a.price - b.price;}return a.name.localeCompare(b.name);});}, [products, search, category, sortBy]);return (<div className={darkMode ? "dark" : "light"}>{/* Search, filters, sorting and product list */}</div>);}
The important optimization is:
darkMode changes↓ProductDashboard renders↓products/search/category/sort unchanged↓visibleProducts reused
Quick Cheat Sheet
Import
import { useMemo } from "react";
Basic usage
const result = useMemo(() => calculateValue(data),[data]);
Multiple dependencies
const result = useMemo(() => calculateValue(data, filter),[data, filter]);
Expensive filtering
const filtered = useMemo(() => filterProducts(products, search),[products, search]);
Expensive sorting
const sorted = useMemo(() => [...items].sort(compareItems),[items]);
Stable object
const options = useMemo(() => ({pageSize: 20}),[]);
Avoid unnecessary memoization
Prefer:
const fullName = `${firstName} ${lastName}`;
over:
const fullName = useMemo(() => `${firstName} ${lastName}`,[firstName, lastName]);
Remember
useMemo↓Memoize a valueuseCallback↓Memoize a functionuseState↓Manage stateuseEffect↓Synchronize external systemsuseRef↓Persist mutable non-UI values