How to Use the useMemo Hook in React to Build Complex Components that are Efficient

Many tutorials on the internet will extensively cover the useState hook or the useEffect hook, however, many tutorials do not get into the more nuanced React hooks such as useMemo .

Before getting into the technical details of the useMemo hook, it’s important to understand the purpose behind using the useMemo hook.

Understanding the Need For `useMemo`

The main purpose behind theuseMemo hook is to help with optimizing React components with heavy computations.

If any part of a React component is updated, it causes the entire component tree to re-render as well. If any of that re-rendering is expensive and takes up a lot of resources, it can hurt the performance of the React component.

For example, imagine that you are building a container component that will allow a company administrator to see all of their employees. That’s what the EmployeeSearchContainer is down below.

Initially, the useEffect on line 12 will fetch our data from some API, which returns 5,000 records for our example. From there, we have a search bar and a EmployeesTable component that handles showing the employees.

Notice that the filteredEmployees being passed into the EmployeesTable component is referencing the output of a function. Anytime that EmployeeSearchContainer re-renders, it will also re-render the EmployeesTable and cause the filteredEmployees function to run again.

// The filteredEmployees value comes as result of a function
<EmployeesTable employees={filteredEmployees} onSelectEmployee={setSelectedEmployee} />

For example, if you click on some employee to select it, the EmployeesTable component will call the setSelectedEmployee function. Since a state variable changed in the EmployeeSearchContainer component, it will cause the entire component tree to re-render and that includes the EmployeesTable . So essentially, if you select an employee, you’re going to cause the filteredEmployeesfunction to run again, resulting in the code going through all of the employees in the employees array unnecessarily.

Running the filteredEmployees over and over again may not be a problem if you’ve only got a few employees to go through, however, if you’ve got 5,000 employees, it can take some time to get through the function. Whenever you select an employee, the UI may get laggy as it tries to finish up the filteredEmployees function. In this case, the filteredEmployees function is an expensive function.

Expensive functions can be costly in either time, memory or processing power. For example, iterating through thousands of objects in an array while performing some expensive operation on each object is an example of a function that is expensive in terms of processing power.

So now we have a problem. If we select an employee, we run the filteredEmployees function unnecessarily. We don’t want to run the function whenever we select an employee because we know that selecting an employee doesn’t really control which employees are filtered. The searchText variable is what the filteredEmployees function depends on.

This problem can be solved with the useMemo hook. React uses memoization under the hood to make the hook work, so let’s go over how memoization works before we get into the solution for making

Understanding Memoization

Memoization is essentially an optimization technique that passes a complex function to be memoized. The result is “memo”-rized when the same parameters are passed in subsequently.

To put it into simple terms, imagine that someone asked you something rather complex. For example, someone asks you, “What is 16 multiplied by 21?” Unless you’re some human calculator, you might have to take some time to come up with the answer. After a few seconds, you come up with the answer and reply with “336”.

If someone asks you to do that same calculation again, you‘re not going to recalculate what 16 x 21 is. You just did that calculation and it took you a while. Your brain loves to be efficient as well and so you memorized that if you see 16 is multiplied by 21, you can immediately just say 336 without having to calculate it in your head.

But what happens if someone asks you a different question right after that? They ask you, “Okay so what is 18 multiplied by 21?” Oh crap, the inputs to the function are no longer the same and you can’t just shout out “336”. So you’re forced to redo the calculation then.

Anatomy of the useMemo Hook

The useMemo hook takes in a function and an array of dependencies.

const someFunction = () => { ... };
const dependencies = [];
useMemo(someFunction, dependencies);

The dependencies act similar to arguments that are passed into a function. Each item in the array is what the useMemo hook “watches” and “listens” to determine if the function should run.

If there are no changes to any of the items in the dependency list, the function result will stay the same and return a cached value. This is similar to our example of 16 being multiplied by 21. The values of the first and second numbers are our dependencies. If they don’t change, we don’t have to re-calculate the value and we just return the value we have in memory.

If there is some change to some item in the dependency list, the function passed into the hook will re-run. So in our example, when we get asked 18 multiplied by 21, the first number changed and so we have to redo the calculation in our head.

This ability to cache the value of an expensive function can be optimal if the function that you pass in is large and expensive.

Downside of Memoization

When you memoize something, you store the result in memory. This allows you to free up the time the CPU would’ve spent on unnecessary computations, however, now your memory resources are being consumed.

So in exchange for space on your computer’s memory, you get to save your CPU from unnecessarily running code. So there’s a cost for using the useMemo hook. As with nearly everything in engineering, there is no “free lunch” and you need to exchange resources for being able to use memoization.

Use the useMemo hook only for really expensive functions. If the savings you get in time or processing power is more than the cost of memoizing the output, then it makes sense to use the useMemo hook, otherwise, it can actually worsen the performance of your React app.

You can use profiling tools to identify which functions are expensive and running unnecessarily. From there, you can use the useMemo hook to optimize the performance.

Another downside to keep in mind about the useMemo hook is that there is no guarantee that the function you pass to the useMemo hook will run ONLY after something changes in your dependency list. React mentions that if the memory on the computer that is running the app starts to run out, React may run the function anyways in order to make space in memory.

Improving Upon the Example

Now that we understand how memoization works and what the useMemo hook can be used for, let’s go over our EmployeeSearchContainer example and see what changes we should make.

First of all, we want to make sure that the filteredEmployees function is not re-run whenever we select an employee and only run when the searchText changes.

So, let’s wrap our filteredEmployees function with the useMemo hook like the following:

const filteredEmployees = useMemo(() => {
if (searchText && searchText.length > 0) {
return employees.filter(employee => {
if (employee.name.includes(searchText) || employee.email.includes(searchText)) {
return employee.isActive;
} else {
return false;
}
});
} else {
return employees;
}
}, [searchText]);

Now the function will only re-run whenever the searchText variable updates. So even if we select an employee, which causes the EmployeeSearchContainer to re-render because the selectedEmployee state variable changes, the filteredEmployees function will return the array of employees from memory.

The full code would look like the following:

Conclusion

The useMemo hook is mainly used when you want to store the value of a function in memory for the same set of inputs. It can help with increasing the performance of your React components as it can cache values that come from computationally heavy functions.

However, it is important that you keep in mind when and when not to use the useMemo hook. If you overuse the hook in an incorrect manner, you will just end up introducing hard-to-solve bugs and hurt the performance of your React app.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Sunny Singh

Sunny Singh

Full-Stack Developer and Technical Writer