The useMemo hook in React is a powerful tool for optimizing performance by memoizing expensive computations. By using useMemo, you can avoid unnecessary recalculations, especially in large and complex applications where performance is critical. This chapter explores the useMemo hook in depth, from basic usage to advanced techniques, with practical examples to demonstrate its benefits.
The useMemo
hook returns a memoized value, which is recomputed only when one of its dependencies changes. It helps to optimize performance by skipping expensive calculations if the inputs (dependencies) haven’t changed.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Let’s start with a simple example to understand the basic usage of useMemo
.
import React, { useState, useMemo } from 'react';
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const expensiveCalculation = (num) => {
console.log('Calculating...');
for (let i = 0; i < 1000000000; i++) {} // Simulate expensive calculation
return num * 2;
};
const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);
return (
useMemo Example
Count: {count}
Expensive Calculation Result: {memoizedValue}
setText(e.target.value)}
placeholder="Type something"
/>
);
}
export default App;
count
and text
.expensiveCalculation
function simulates a CPU-intensive task.useMemo
memoizes the result of expensiveCalculation(count)
and recalculates it only when count
changes.memoizedValue
is displayed alongside the count and a text input.When you run this application, you’ll see the count and the result of the expensive calculation. The console will log “Calculating…” only when the count changes, not when the text input is updated.
In this example, we will use useMemo
to efficiently filter a large list of items based on user input.
import React, { useState, useMemo } from 'react';
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
function App() {
const [search, setSearch] = useState('');
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter((item) =>
item.toLowerCase().includes(search.toLowerCase())
);
}, [search]);
return (
useMemo Filtering Example
setSearch(e.target.value)}
placeholder="Search items"
/>
{filteredItems.map((item, index) => (
- {item}
))}
);
}
export default App;
items
) with 10,000 elements.filteredItems
array is computed using useMemo
and recalculated only when search
changes.search
state is updated via an input field, and the filtered items are displayed in a list.When you run this application, you’ll see a search input and a list of items. The console will log “Filtering items…” only when the search input changes, demonstrating efficient filtering of the large data set.
In this example, we will memoize a component that performs an expensive render operation to optimize performance.
import React, { useState, useMemo } from 'react';
function ExpensiveComponent({ count }) {
console.log('Rendering ExpensiveComponent...');
for (let i = 0; i < 1000000000; i++) {} // Simulate expensive render
return Expensive Component Count: {count}
;
}
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const memoizedComponent = useMemo(() => {
return ;
}, [count]);
return (
useMemo Component Example
{memoizedComponent}
setText(e.target.value)}
placeholder="Type something"
/>
);
}
export default App;
ExpensiveComponent
simulates an expensive render operation.useMemo
memoizes the ExpensiveComponent
and re-renders it only when count
changes.When you run this application, you’ll see the count and the expensive component. The console will log “Rendering ExpensiveComponent…” only when the count changes, not when the text input is updated, demonstrating optimized rendering.
useMemo
can also be used to memoize callbacks, though useCallback
is more commonly used for this purpose. However, let’s see how useMemo
can achieve the same.
import React, { useState, useMemo } from 'react';
function Child({ onClick }) {
console.log('Rendering Child...');
return ;
}
function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const memoizedCallback = useMemo(() => {
return () => {
setCount((prevCount) => prevCount + 1);
};
}, []);
return (
useMemo Callback Example
Count: {count}
setText(e.target.value)}
placeholder="Type something"
/>
);
}
export default App;
memoizedCallback
is memoized using useMemo
and recalculated only once because it has an empty dependency array.Child
component uses the memoized callback.Child
component is re-rendered only when memoizedCallback
changes.When you run this application, you’ll see a button and a count. The console will log “Rendering Child…” only once, demonstrating the memoized callback’s effectiveness.
In this example, we will use useMemo
to memoize the result of a computation-heavy function. This will help us avoid re-computation when the input data hasn’t changed.
import React, { useState, useMemo } from 'react';
function App() {
const [count, setCount] = useState(0);
const [otherCount, setOtherCount] = useState(0);
const expensiveCalculation = (num) => {
console.log('Performing expensive calculation...');
for (let i = 0; i < 1000000000; i++) {} // Simulate expensive calculation
return num * 2;
};
const memoizedValue = useMemo(() => expensiveCalculation(count), [count]);
return (
useMemo Example: Expensive Calculation
Count: {count}
Other Count: {otherCount}
Expensive Calculation Result: {memoizedValue}
);
}
export default App;
count
and otherCount
.expensiveCalculation
function simulates a CPU-intensive task.useMemo
memoizes the result of expensiveCalculation(count)
and recalculates it only when count
changes.memoizedValue
is displayed alongside the count and other count values.When you run this application, you’ll see the counts and the result of the expensive calculation. The console will log “Performing expensive calculation…” only when the count changes, not when the other count is updated.
In this example, we will use useMemo
to efficiently filter a large list of items based on user input. This can help to optimize performance when working with large data sets.
import React, { useState, useMemo } from 'react';
const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);
function App() {
const [search, setSearch] = useState('');
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter((item) =>
item.toLowerCase().includes(search.toLowerCase())
);
}, [search]);
return (
useMemo Example: Filtering Large List
setSearch(e.target.value)}
placeholder="Search items"
/>
{filteredItems.map((item, index) => (
- {item}
))}
);
}
export default App;
items
) with 10,000 elements.filteredItems
array is computed using useMemo
and recalculated only when search
changes.search
state is updated via an input field, and the filtered items are displayed in a list.When you run this application, you’ll see a search input and a list of items. The console will log “Filtering items…” only when the search input changes, demonstrating efficient filtering of the large data set.
The useMemo hook in React is essential for optimizing performance by memoizing expensive computations and preventing unnecessary recalculations. By understanding and effectively using useMemo, you can enhance the efficiency of your React applications, especially in scenarios involving large data sets, complex calculations, and expensive renders. Happy coding !❤️