Performance optimization with useMemo
Tags: react, webdev, performance, javascript
Published: 2024-06-09 17:08:15

One of those things that you don't know enough, and then when you know somewhat you tend to use a bit too much is React beloved and behated <a href="https://react.dev/reference/react/useMemo" target="_blank">useMemo hook</a>. In my quest of trying to understand more the features of React that I use on a regular basis, I find myself in need to write more about this hook, clarifying what is it, when to use it and when not.

So, let's get started and unravel this madness!

## What is useMemo?

Introduced in React 16.8, the useMemo hook is a built-in feature designed to memoize values and prevent unnecessary recalculations and re-renders, meaning that instead of calculating things on the fly, it will first check if that computation has already done and take the value already stored.

... Wait wait wait! __memoization what?!__ What's that?!

<a href="https://www.freecodecamp.org/news/author/gercocca/" target="_blank">Germán Cocca</a> makes a great job at explaining this concept <a href="https://www.freecodecamp.org/news/memoization-in-javascript-and-react/" target="_blank">in his article about Memoization in Javascript and React</a>, and I will not try to rewrite it when he's already told it better than I ever can!

<blockquote>
<p>In programming, <strong>memoization is an optimization technique</strong> that makes applications more efficient and hence faster. It does this by storing computation results in cache, and retrieving that same information from the cache the next time it's needed instead of computing it again.</p>
<br />
<p>In simpler words, <mark><strong>it consists of storing in cache the output of a function</strong>, and making the function check if each required computation is in the cache before computing it.</mark></p>
<br />
  <div class="text-right">— <a href="https://www.freecodecamp.org/news/author/gercocca/" target="_blank">Germán Cocca</a> in <a href="https://www.freecodecamp.org/news/memoization-in-javascript-and-react/" target="_blank">What is Memoization? How and When to Memoize in JavaScript and React</a></div>
</blockquote>

So far so good. Yes?! __Nope__.

I have heard that *useMemo should not always be used* and this explanation makes me think instead that it always sounds like a good idea, so let's continue digging up a bit more more, but let's go the long route and let's see first how to implement it.

## How to use useMemo

The basic syntax for useMemo is

```javascript
const memoizedValue = useMemo(() =>
computeExpensiveValue(a, b), [a, b]);
```

This line of code contains the function used to calculate the value and the dependency array. For example, consider a component that filters a list of items based on a search term:

```javascript
import React, { useMemo } from 'react';

const FilteredList = ({ items, searchTerm }) => {
  const filteredItems = useMemo(() => {
    return items.filter(item => 
item.toLowerCase()
.includes(searchTerm.toLowerCase())
);
  }, [items, searchTerm]);

  return (
    <ul>
      {filteredItems.map(item => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
};

```

In this example, we use useMemo to memoize the filtered list of items and avoid recalculating the filtered list every time the component renders. *Still sounds pretty good and still like I should actually be using it more than I actually do.* 🤔

Browsing various examples online, as you can see from the first snippet of code in this article, however, I find the emerging written pattern of using useMemo *only for expensive calculations*, *computed expensive value*, *expensive operations*, and I finally start understanding that there is something particularly in mind for which useMemo is recommended.

But what is an expensive calculation in JavaScript?

## Expensive calculations

Expensive calculations are __tasks that require a significant amount of processing time and resources__: they might be time-consuming, memory-intensive, or computationally complex. What seems to fit in this category is:

- __Manipulating large strings__, such as parsing and transforming text
- __Transforming large datasets through iteration__, while performing multiple calculations on the data
- __Sorting or filtering large arrays__
- __Fetching data through APIs/databases__
- __Processing and manipulating big files__
- __Rendering a top component__ with a significant number of nested components and elements
- __Recursive algorithms__, advanced statistical calculations, matrices, weird formulas etc. etc. etc.


<strong>Note:</strong>
<a href="https://react.dev/reference/react/useMemo" target="_blank">React useHook official documentation</a> has a section <a href="https://react.dev/reference/react/useMemo#how-to-tell-if-a-calculation-is-expensive" target="_blank">How to tell if a calculation is expensive?</a> where it teaches us to use the console by adopting <code>console.time()</code> and <code>console.timeEnd()</code> to better determine the cost of our operations.

Not all these operations are inherently worthy of useMemo though! There are at least a few things to consider.

<br /><br />

### 1. How often is the operation performed?

If the operation is performed frequently or triggered by state/props changes, and the result doesn't change as often, memoizing the result can prevent redundant recalculations, therefore improving overall performance.

For example, if you have a big array of strings that needs to be formatted in a particular way, and you might want to list all of them in a nice HTML list, using useMemo might help avoiding slow renders in the UI.
<br />

### 2. How reusable is the result of useMemo?

The result is used within the component or passed down as props to child components; if the result is used multiple times, useMemo can indeed help avoid unnecessary recalculation.

Think of a large file that you want to use in different sections of your app; it's definitely worth contemplating memoizing it.
<br />

### 3. Is it worth the memory consumption?

As the memoized values are stored in memory until the component unmounts or the dependencies change, memoizing large datasets will definitely increase memory consumption; __if the fetching from caching is faster than the recomputation of the calculations, perhaps useMemo might have a valid purpose__.

If you're fetching a big list and your interface freezes, perhaps yes, it's time to think about useMemo.

<hr/>

In all of the above cases, and even more critical than for `useEffect`, __the dependencies of the memoized function need to be properly evaluated__ to make sure that useMemo recalculates the memoized value only when necessary, as performance might otherwise ironically drop.

In his article <a href="https://maxrozen.com/understanding-when-use-usememo" target="_blank">Understand when to use useMemo</a>, <a href="https://twitter.com/RozenMD" target="_blank">Max Rozen</a> puts it up in simple words:
<br />
<blockquote>
  <p><mark>Don't use useMemo until you notice parts of your app are frustratingly slow.</mark> <strong>Premature optimisation is the root of all evil</strong>, and throwing useMemo everywhere is premature optimisation.</p>
— <a href="https://twitter.com/RozenMD" target="_blank">Max Rozen</a> in <a href="https://maxrozen.com/understanding-when-use-usememo" target="_blank">Understand when to use useMemo</a>
</blockquote>
<br />

 
## <br />"Premature optimization is the root of all evil"
*This paragraph might be a tad too long and expanding on a topic that is not quite the purpose of this article; feel free to move to the next header if you already have a grasp about premature optimization.*

Calling premature optimization *the root of all evil* seems to be something rather common, and <a href="https://effectiviology.com/itamar-shatz/" target="_blank">Dr. Itamar Shatz</a> in his article about <a href="https://effectiviology.com/premature-optimization/" target="_blank">Premature Optimization: Why It’s the “Root of All Evil” and How to Avoid It</a> says that<br /><br />

<blockquote>
  <p><strong>Premature optimization</strong> involves trying to <mark><strong>improve something</strong> — especially with the goal of perfecting it — <strong>when it’s too early to do so</strong>.</mark></p>

— <a href="https://effectiviology.com/itamar-shatz/" target="_blank">Dr. Itamar Shatz</a> in <a href="https://effectiviology.com/premature-optimization/" target="_blank">Premature Optimization: Why It’s the “Root of All Evil” and How to Avoid It</a>
</blockquote>
<br />

His article also shows that the famous *Premature optimization is the root of all evil* sentence is a quote from the computer scientist <a href="https://profiles.stanford.edu/donald-knuth" target="_blank">Donald Knuth</a>, from his article <a href="https://dl.acm.org/doi/10.1145/356635.356640" target="_blank">Structured Programming with go to Statements</a>. The article is a reminder to developers not to spend too much time or resources optimizing parts of a program before they need to and especially before they have a working version.

According to Donald Knuth, premature optimization leads to wasted effort, as you might end up optimizing parts of the code that don't have a significant impact on the overall performance, or even optimizing code that eventually gets removed or significantly changed.

His recommendations are to __first build a functional system__, __then identify any performance issues__, and __finally optimize critical parts that actually need optimization__, without forgetting that readability and maintainability are often more important than minor efficiency gains, especially in early development stages.

Dr. Itamar Shatz lists out the <a href="https://effectiviology.com/premature-optimization/#Dangers_of_premature_optimization" target="_blank">dangers of premature optimization</a> in a few yet impactful points, which I recommend reading from his article.

<hr />

I have now some ideas on where useMemo could come at hand as well as potential dangers of premature optimization, but I still feel like the constraints for which it's recommended are still a bit unable to give a clear line. Perhaps it'd be beneficial to go the other route and analyze when _not_ to use useMemo.
<br />
## When not to use useMemo

By what we've figured out before, if an operation is relatively simple, doesn't consume much processing time, and doesn't depend on frequently changing data, __using useMemo will add unnecessary overhead without significant performance gains__.

Therefore

- if a component **doesn't have any expensive calculations** and **doesn't render frequently**
- or the expensive operation is **isolated to a specific part of the component that doesn't impact the overall rendering performance**
- or the expensive operation varies significantly between the instances where the component is used
- and we're trying to accommodate all sorts of devices that might not have a lot of memory available
- and **the performance improvement is not noticeable by end-users**

... __we might find ourselves perhaps not in need of useMemo__.

We also need to consider that adding useMemo to memoize some calculations can make writing unit tests more complicated, and while it's not a reason to avoid useMemo, it's definitely worth to weigh the benefits of memoization also against the ease of testing and maintaining the code.
<br />
## Example implementations

From all this reading and writing, I can deduct that there are plausible usages where you expect to process a lot of data, that won't change as often, such as:

```javascript
// Plausible usage of useMemo
import React, { useMemo } from 'react';

const BigProcessedList = ({ data }) => {
  const processedData = useMemo(() => {
    return data.map((item) => {
      // Simulating an expensive computation
      for (let i = 0; i < 1e6; i++) {}
      return item * 2;
    });
  }, [data]);

  return (
    <ol>
      {processedData.map((item, index) => (
        <li key={`${index}__${item}`}>{item}</li>
      ))}
    </ol>
  );
};

```

Another plausible example would be:

```javascript
// Plausible good use of useMemo
import React, { useMemo } from 'react';

const EmployeeList = ({ employees, filter }) => {
  const filteredEmployees = useMemo(() => {
    return employees.filter(emp => emp.department === filter);
  }, [employees, filter]);

  return (
    <ul>
      {filteredEmployees.map((employee) => (
        <li key={employee.id}>{employee.name}</li>
      ))}
    </ul>
  );
};
```

... And potentially, there could be so many bad/unrecommended implementations where there's no big computation needed and therefore useMemo is bringing no real benefit to the end user:

```javascript
// Unoptimal use of useMemo
import React, { useMemo } from 'react';

const CombinedStringComponent = ({ propA, propB }) => {
  const combinedProps = useMemo(() => {
    return `${propA} ${propB}`;
  }, [propA, propB]);

  return (
    <div>{combinedProps}</div>
  );
};
```

Ultimately, the trend with useMemo seems to be that as soon as people get to know more about it, they want to implement it, whereas __it'd make more sense to leave it for only when the app clearly shows benefits from implementing it__. It sounds like a pretty good news to me as it means that most likely I don't have to change anything on my website as I don't seem to see anything particularly too slow... *yay!* 😂

I've truly enjoyed writing about this and I can't help but recommend strongly to read the sources/resources attached to the article; if you feel like I've miswritten something (which can very well be the cause as my best corrections often come days after I rewrote something), don't hesitate to mention that in the comments below!

But before I leave...
<br />
## A brief note about useMemo vs. useCallback

You'll also hear this one too! And while `useCallback` is gonna be a topic for another day, let's just iron this one last thing out before we call it a day!

__useCallback__ is similar to useMemo, but __instead of memoizing the result of a function, it memoizes the actual function itself__, which is particularly useful when you have a component that receives a function as a prop and you want to prevent unnecessary re-rendering of child components that depend on that function. This means that if the dependencies of useCallback don't change, __the same function instance is used, avoiding unnecessary re-renders of child components that depend on it__.

Now out! Bye! :D
<br />
## Sources and inspiration
- <a href="https://react.dev/reference/react/useMemo" target="_blank">Official React useHook documentation</a>
- <a href="https://chat.openai.com/" target="_blank">ChatGPT</a> prompt: `Can you help me write a good article on the What, Why and How of using useMemo hook?`
- <a href="https://www.w3schools.com/react/react_usememo.asp" target="_blank">W3School documentation on useMemo</a>
- <a href="https://www.freecodecamp.org/news/memoization-in-javascript-and-react" target="_blank">What is Memoization? How and When to Memoize in JavaScript and React</a> by <a href="https://www.freecodecamp.org/news/author/gercocca/" target="_blank">Germán Cocca</a> from <a href="https://www.freecodecamp.org/" target="_blank">freeCodeCamp</a>
- <a href="https://maxrozen.com/understanding-when-use-usememo" target="_blank">Understand when to use useMemo</a> by <a href="https://twitter.com/RozenMD" target="_blank">Max Rozen</a>
- <a href="https://effectiviology.com/premature-optimization/" target="_blank">Premature Optimization: Why It’s the “Root of All Evil” and How to Avoid It</a> by <a href="https://effectiviology.com/itamar-shatz/" target="_blank">Dr. Itamar Shatz</a>
- Cover: <a href="https://www.freepik.com/free-vector/gradient-liquid-abstract-background_13346239.htm" target="_blank">Vector Liquid Background</a> by <a href="https://www.freepik.com/author/pikisuperstar" target="_blank">pikistar</a> via <a href="https://freepik.com" target="_blank">Freepik</a>, <a href="https://www.freepik.com/free-psd/3d-illustration-people-working-marketing_42937458.htm" target="_blank">People working marketing</a> by <a href="https://freepik.com" target="_blank">Freepik</a>
<hr />
Originally posted in <a href="https://oh-no.ooo">oh-no.ooo</a> (<a href="https://www.oh-no.ooo/articles/performance-optimization-with-usememo">Performance optimization with useMemo</a>), my personal website.