useMemo is a hook introduced in React 16.8. You use it to boost performance in React functional components by skipping costly calculations on every render. This topic will show you the useMemo syntax and how to use it, with examples.
What is memoization?
Before we start learning about useMemo, let's understand the term memoization: Memoization is a programming technique used to speed up computer programs by storing the results of costly function calls and reusing them when the same inputs come up again.
In simpler terms, imagine you have a function that does a complex calculation. When you first call this function with a certain set of inputs, it performs the calculation and gives you the result. If you call the function again with the same inputs, it won't recalculate the result; it will just give you the stored result from the last calculation. This can save a lot of processing time, especially for functions that take a lot of computing power and get called often with the same inputs.
However, memoization has a downside. While it can greatly speed up your program by reducing unnecessary calculations, it also uses more memory because it has to keep the results of past function calls. This balance between time and space is a common issue in computer science and programming.
How to use useMemo
The useMemo hook lets you memoize expensive function calls and only recompute the memoized value when one of the dependencies changes. This is very useful when you are rendering big lists or doing heavy calculations.
Here's the syntax:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);The useMemo takes a callback function and a list of dependencies. In this snippet, computeExpensiveValue(a, b) is a function that does an expensive calculation with the variables a and b. This function will only run again if the values of a or b change between renders. Then the result of computeExpensiveValue(a, b) is stored in memoizedValue.
The useMemo hook is like useCallback, which you will learn about in later topics; the main difference is that useMemo gives you the result of the function as a memoized value, while useCallback gives you the function itself as a memoized reference. You use useMemo when you need to remember and return the result of a function that takes a lot of work to compute, and you use useCallback to remember and return the function itself, mostly for event handlers or callbacks.
Let's look at more examples of its use. Imagine you have a component that sorts a list of numbers and this component re-renders often for other reasons, like user interactions. Sorting the list can take a lot of time if the list is big, so it makes sense to memoize the sorted list.
import React, { useMemo } from 'react';
function MyComponent({ numbers }) {
const sortedNumbers = useMemo(() => {
console.log('Sorting numbers...');
return numbers.slice().sort((a, b) => a - b);
}, [numbers]);
return (
<ul>
{sortedNumbers.map((number) => (
<li key={number}>{number}</li>
))}
</ul>
);
}
import MyComponent from './MyComponent';
function App() {
const numbers = [5, 2, 8, 3, 1, 9, 4, 7, 6];
return (
<div>
<h1>Numbers:</h1>
<MyComponent numbers={numbers} />
</div>
);
}
export default App;
Here, sortedNumbers will only be recalculated when numbers changes. If numbers stays the same over several renders, the previously sorted list will be used, and you won't see "Sorting numbers..." in the console.
When the App component renders, it passes the numbers array to the MyComponent as a prop. The first time, the useMemo function runs and sorts the numbers array. The sorted result is then kept in the sortedNumbers variable.
After that, the sortedNumbers array shows up as a list of <li> elements. If the numbers prop changes, the useMemo hook runs again. However, since the numbers array is the only thing listed as a dependency, the sorting will only happen if the numbers prop has changed. If not, the old memoized value is reused, and sorting gets skipped.
Let's think about a more complex example where we have a list of users and we want to filter them by name:
import React, { useMemo } from 'react';
function UserList({ users, filter }) {
const filteredUsers = useMemo(() => {
console.log('Filtering users...');
return users.slice().filter(user => user.name.includes(filter));
}, [users, filter]);
return (
<ul>
{filteredUsers.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}In this case, filteredUsers will only be recalculated when users or filter changes. If they stay the same across several renders, you'll use the filtered list from the last render, and you won't see "Filtering users..." in the console.
In both examples, you use the useMemo hook to prevent doing expensive tasks (like sorting and filtering) every time you render, which significantly helps your React app perform better.
Why to use useMemo
The useMemo hook in React is used in various scenarios. As you saw in the previous section, one common reason to use useMemo is when you have a function that carries out a heavy computation. You can look at the previous examples for this case.
Another scenario is referential equality. This is important when you want to keep the same reference for a value between renders, especially when passing objects, arrays, or functions as props or when you use them as dependencies for other hooks like useEffect or useCallback. Without useMemo, a new reference would be made on every render, leading to unwanted re-renders or effects. Although the previous examples partly show this, let's see another one to be clear:
import React, { useMemo, useEffect } from 'react';
function ParentComponent({ data }) {
const transformedData = useMemo(() => data.map(item => ({...item, extraProp: 'extra'})), [data]);
return ;
}
function ChildComponent({ data }) {
// This effect will only run when data really changes, not on every render
useEffect(() => {
console.log('Data changed');
}, [data]);
// render code here...
}Above, the transformedData array will keep the same reference between renders unless the data reference changes. This ensures that the useEffect hook in ChildComponent only runs when the data reference actually changes, not on every render. Without useMemo, a new transformedData array would be made on each render, causing the useEffect hook to run when it shouldn't.
Another reason is if you have a function that takes a while to return. It might be smart to use useMemo. That way, the function will only be called again if its dependencies have changed.
Similarly, if a component is detailed and takes a long time to render, you can use useMemo to remember the output and make the rendering quicker.
Keep in mind, while useMemo can be very handy, it's not always needed and can sometimes add unneeded complexity or extra work in simple cases. Now, let's learn when not to use it.
Considerations and limitations of useMemo
While useMemo can be handy in some situations, there are times when it's better not to use it. Here are some cases where you should avoid using useMemo:
Simple Calculations or Conditions: If you're doing an easy computation or operation (like adding two numbers or checking a boolean),
useMemomight slow down your code. The extra work of usinguseMemo(which includes memory allocation for storing the memoized value and additional function calls) can be more than the time you save from skipping the computation.When Dependencies Change Frequently: If the dependencies of
useMemochange a lot, you'll have to recompute the memoized value often, which can wipe out any performance gains. In these cases,useMemomight slow down your app because of the extra overhead of recomputing and storing the value.When Memory Usage is a Concern:
useMemokeeps the computed value in memory between renders. If the value is big (like a huge array or object) and your component re-renders a lot, this could lead to more memory use.For Side Effects:
useMemoisn't for side effects. The function you give touseMemoshould be pure, that is, it shouldn't change anything outside its scope. For side effects, you should useuseEffectinstead.
Note that rushing into optimization can make your code more complicated and is generally not a good idea. It's best to write code that's correct, clear, and simple first, and then tune the parts that need it based on performance profiling.
Conclusion
The useMemo hook in React is a tool that can improve the performance of your application. It does this by remembering the result of computations, keeping the same references across renders. You'll find it handy for heavy computations, when you need stable references, when calling expensive functions, and for components that take a long time to render.
But it's important to remember that useMemo isn't a fix for all performance problems. Using it can actually slow things down if not needed. It's not great for easy calculations, things that change often, if you're worried about using too much memory, or for side effects.
You should first aim to write code that's easy to understand and works correctly. Only if you start seeing performance issues might you want to use tools like useMemo.