Telerik blogs

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.

A Short Review of React Hooks

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.

Harry Potter meme asking why is it always useEffect, useMemo, and useCallback

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 Basics of the useMemo Hook

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:

  1. Define a function that computes the value you want to memoize.
  2. Wrap the function in the useMemo hook.
  3. Pass the dependencies (i.e., the values on which the memoized value depends) as the second argument to useMemo.
  4. Use the memoized value in your component.
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).

Avoiding Common useMemo Mistakes

While useMemo can be a powerful tool, it’s essential to use it correctly. Here are some common mistakes to avoid:

Passing Unnecessary Dependencies

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.

Memoizing Large or Complex Values

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.

Using useMemo Excessively

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.

Putting useMemo to Work

Now that you understand how useMemo works, let’s look at a few examples of how you can use it in your code.

Memoizing a Filtered Array

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.

Memoizing an Expensive Computation

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.

Conclusion

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 Maldonado
About the Author

Leonardo Maldonado

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.

Related Posts

Comments

Comments are disabled in preview mode.