Dive deep into React’s useMemo hook and learn when to use it, when to avoid it, and what mistakes to steer clear of so you can reduce expensive computations.
If you’re a React developer, you’re probably familiar with hooks. And if you’re a meticulous developer, you want to ensure your code is optimized for performance. That’s
where the useMemo
hook comes in.
In this article, we’ll explore the useMemo
hook and show you how to use it effectively to boost the performance of your React application.
React Hooks were introduced in React 16.8. It was one of the most essential releases of React, and it changed the way we build modern applications. Being able to manage state inside functional components was brilliant, making our applications more readable, scalable, and reusable. We could create custom hooks usable across different components.
Of course, this was a while ago. The community has since been evolving and creating new things.
Several built-in hooks are available in React, including useState
, useMemo
, useEffect
and useCallback
. Each hook serves a specific purpose and can be used to enhance
the functionality of your components.
The useState
hook is the simplest—it’s used to manage the state of functional components. You’ve worked with it so often that we don’t have to say much about it, so l et’s talk about the three hooks that scare
people and make their lives harder.
The useEffect
, useMemo
and useCallback
hooks are considered more complex to understand and use than other hooks because they are built to solve more complex needs.
We don’t have time to discuss all three in this article, but we will dive deep into the useMemo
hook specifically.
The useMemo
hook is built into React, allowing you to memoize a value. In other words, useMemo
caches the result of a function and returns the cached value whenever it’s called again with the same arguments. Caching
optimizes performance and reduces unnecessary computations, which is particularly useful when you have expensive computations that are triggered frequently.
Here’s how you can use useMemo
:
useMemo
hook.useMemo
.const cachedValue = useMemo(calculateValue, dependencies)
One thing to remember with useMemo
is that it’s only sometimes necessary. If the computation is not expensive or doesn’t need to be re-computed frequently, you might not need to use this hook. Using useMemo
unnecessarily
can hurt performance, as it adds overhead to your component.
Another thing to remember is that useMemo
only memoizes the result of the function. If the function has side effects (i.e., it modifies state or interacts with the DOM), they will still occur every time the function is called, even if the
result is memoized. In those cases, you should use the useCallback
hook instead, which memoizes the entire function (including its side effects).
While useMemo
can be a powerful tool, it’s essential to use it correctly. Here are some common mistakes to avoid:
If you include unnecessary dependencies, the memoized value will be recomputed unnecessarily, which can hurt performance. Make sure to memoize only the dependencies that affect the value.
import React, { useMemo } from 'react';
const UserComponent = ({ firstName, lastName }) => {
const fullName = useMemo(() => {
return `${firstName} ${lastName}`;
}, [firstName, lastName]);
return <div>{fullName}</div>;
};
export default UserComponent;
Whenever you use React hooks with an array of dependencies (not exclusive for useMemo
), it’s essential to include the dependencies that directly impact the memoized value. In this case, you should only have [firstName, lastName]
in the dependency array to avoid unnecessary recomputations when irrelevant values change.
If a value is large or complex, memoizing it can decrease performance rather than improve it. This is because the useMemo
hook must compute and cache the value, which can be time-consuming.
import React, { useMemo } from 'react';
const UserDataComponent = ({ data }) => {
const processedData = useMemo(() => {
// Perform some heavy computations or transformations on user data
// ...
return processedData;
}, [data]);
return <div>{processedData}</div>;
};
export default UserDataComponent;
Imagine that the data prop is extensive, an object with multiple objects and many other structures inside. In that case, this caching process can take significant time and resources, potentially decreasing performance rather than improving it.
When the value being memoized is large or involves complex computations, we should remember that memoization is most effective when applied to values that are expensive to compute but have relatively stable dependencies.
Although useMemo
can be helpful, it shouldn’t be used excessively. Only use it in cases where you have a computationally expensive function that needs to be memoized.
It’s important to note that useMemo
is not a silver bullet for optimizing performance in your React application. While it can be helpful in some instances, overusing it can lead to decreased performance. It’s essential to use
useMemo judiciously and only when necessary.
It’s also essential to consider which dependencies to include in the array carefully. Including a dependency that doesn’t affect the memoized value can lead to unnecessary re–renders and decreased performance.
Now that you understand how useMemo
works, let’s look at a few examples of how you can use it in your code.
Consider a scenario where you have a long list of items to filter based on a search query. When the user types in the search query, you want to show only the items that match the query. However, computing the filtered list every time the user types in
a new character can be expensive. This is where useMemo
can help.
import React, { useState, useMemo } from 'react';
const FiltersComponent = ({ items, filters }) => {
const [query, setQuery] = useState('');
const filteredItems = useMemo(() => {
const filtered = items.filter((item) =>
item.toLowerCase().includes(query.toLowerCase())
);
return filtered;
}, [items, query, filters]);
const handleSearchChange = (event) => {
setQuery(event.target.value);
};
return (
<div>
<input type="text" value={searchQuery} onChange={handleSearchChange} />
<ul>
{filteredItems.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
};
export default FiltersComponent;
In this example, the filteredItems
array is memoized using useMemo
. The function depends on items and filters, so they’re included in the dependency array. This way, the filteredItems
array is only recomputed
when items or filters change. This can help improve the performance of your application, especially when dealing with large lists.
There are times when you need to perform a computation that is expensive and time-consuming. For example, you might need to perform a complex calculation or iterate over a large dataset. In such cases, memoizing the result can help improve the performance of your application.
import React, { useMemo } from 'react';
const ExpensiveResultComponent = () => {
const expensiveResult = useMemo(() => {
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return result;
}, []);
return <div>{expensiveResult}</div>;
};
export default ExpensiveResultComponent;
In the code example provided, the expensiveResult
is memoized using useMemo
. The function computes a value using a loop that iterates one billion times. By memoizing the result, you can avoid recomputing the value every time
the component re-renders. This can help improve the performance of your application, especially when dealing with computationally intensive tasks.
Overall, useMemo
is a powerful tool that can help you improve the performance of your React application. By memoizing expensive computations or filtered arrays, you can avoid unnecessary rerenders and enhance the responsiveness of your application.
However, it’s essential to use useMemo
correctly and avoid common mistakes that can hurt performance. Hopefully, this article has provided you with a solid understanding of how to use useMemo
once and for all.
Leonardo is a full-stack developer, working with everything React-related, and loves to write about React and GraphQL to help developers. He also created the 33 JavaScript Concepts.