How to useMemo and useCallback: you can remove most of them
Tags: javascript, react, performance, webdev
Published: 2022-06-15 12:36:58


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jyd7e54jn5fgwzxwcgg2.png)

Originally published at [https://www.developerway.com](https://www.developerway.com). The website has more articles like this 😉

...

If you’re not completely new to React, you’re probably already at least familiar with <a href="https://reactjs.org/docs/hooks-reference.html#usememo">useMemo</a> and <a href="https://reactjs.org/docs/hooks-reference.html#usecallback">useCallback</a> hooks. And if you work on a medium to large-scale application, chances are you can describe some parts of your app as an “incomprehensible chain of `useMemo` and `useCallback`s that is impossible to read and debug". Those hooks somehow have the ability to just spread around the code uncontrollably, until they just completely take over and you find yourself writing them just because they are everywhere and everyone around you is writing them.

And do you know the sad part? All of this is completely unnecessary. You can probably remove 90% of all `useMemo` and `useCallbacks` in your app right now, and the app will be fine and might even become slightly faster. Don’t get me wrong, I’m not saying that `useMemo` or `useCallback` are useless. Just that their use is limited to a few very specific and concrete cases. And most of the time we’re wrapping things in them unnecessary.

So this is what I want to talk about today: what kind of mistakes developers make with `useMemo` and `useCallback`, what is their actual purpose, and how to use them properly.

There are two major sources of the poisonous spread of those hooks in the app:

- memoizing props to prevent re-renders
- memoizing values to avoid expensive calculations on every re-render

We’ll take a look at them later in the article, but first: what exactly is the purpose of `useMemo` and `useCallback`?

## Why do we need useMemo and useCallback

The answer is simple - memoization **between re-renders**. If a value or a function is wrapped in one of those hooks, react will cache it during the initial render, and return the reference to that saved value during consecutive renders. Without it, non-primitive values like arrays, objects, or functions, will be re-created from scratch on every re-render. memoization is useful when those values are compared. It’s just your normal javascript:

```js
const a = { "test": 1 };
const b = { "test": 1'};

console.log(a === b); // will be false

const c = a; // "c" is just a reference to "a"

console.log(a === c); // will be true
```

Or, if closer to our typical React use case:

```tsx
const Component = () => {
  const a = { test: 1 };

  useEffect(() => {
    // "a" will be compared between re-renders
  }, [a]);

  // the rest of the code
};
```

`a` value is a dependency of `useEffect` hook. On every re-render of `Component` React will compare it with the previous value. `a` is an object defined within the `Component`, which means that on every re-render it will be re-created from scratch. Therefore a comparison of `a` “before re-render” with `a` “after re-render” will return `false`, and `useEffect` will be triggered on every re-render.

To avoid it, we can wrap the `a` value in `useMemo` hook:

```tsx
const Component = () => {
  // preserving "a" reference between re-renders
  const a = useMemo(() => ({ test: 1 }), []);

  useEffect(() => {
    // this will be triggered only when "a" value actually changes
  }, [a]);

  // the rest of the code
};
```

Now `useEffect` will be triggered only when the a value actually changes (i.e. never in this implementation).

Exactly the same story with `useCallback`, only it’s more useful for memoizing functions:

```tsx
const Component = () => {
  // preserving onClick function between re-renders
  const fetch = useCallback(() => {
    console.log('fetch some data here');
  }, []);

  useEffect(() => {
    // this will be triggered only when "fetch" value actually changes
    fetch();
  }, [fetch]);

  // the rest of the code
};
```

The most important thing to remember here is that both `useMemo` and `useCallback` are useful only **during the re-renders phase**. During the initial render, they are not only useless but even harmful: they make React do some additional work. This means that your app will become **slightly slower** during the initial render. And if your app has hundreds and hundreds of them everywhere, this slowing down can even be measurable.

## Memoizing props to prevent re-renders

Now that we know the purpose of those hooks, let's take a look at their practical usage. And one of the most important ones and the most often used is to memoize props values to prevent re-renders. Make some noise if you’ve seen the code below somewhere in your app:

* Had to wrap `onClick` in `useCallback` to prevent re-renders

```tsx
const Component = () => {
  const onClick = useCallback(() => {
    /* do something */
  }, []);
  return (
    <>
      <button onClick={onClick}>Click me</button>
      ... // some other components
    </>
  );
};
```

* Had to wrap `onClick` in `useCallback` to prevent re-renders

```tsx
const Item = ({ item, onClick, value }) => <button onClick={onClick}>{item.name}</button>;

const Component = ({ data }) => {
  const value = { a: someStateValue };

  const onClick = useCallback(() => {
    /* do something on click */
  }, []);

  return (
    <>
      {data.map((d) => (
        <Item item={d} onClick={onClick} value={value} />
      ))}
    </>
  );
};
```

* Had to wrap `value` in `useMemo`, because it’s a dependency of a memoized `onClick`:

```tsx
const Item = ({ item, onClick }) => <button onClick={onClick}>{item.name}</button>;

const Component = ({ data }) => {
  const value = useMemo(() => ({ a: someStateValue }), [someStateValue]);
  const onClick = useCallback(() => {
    console.log(value);
  }, [value]);

  return (
    <>
      {data.map((d) => (
        <Item item={d} onClick={onClick} />
      ))}
    </>
  );
};
```

Is this something that you’ve done or seen other people around you do? Do you agree with the use case and how the hook solved it? If the answer to those questions is “yes”, congratulations: `useMemo` and `useCallback` took you hostage and unnecessary control your life. In all of the examples, those hooks are useless, unnecessary complicate code, slow down initial render and prevent nothing.

To understand why, we need to remember one important thing about how React works: the reasons why a component can re-render itself.

## Why a component can re-render itself?

“Component re-renders itself when state or prop value changes” is common knowledge. Even <a href="https://reactjs.org/docs/react-component.html#updating">React docs</a> phrase it like this. And I think this statement is exactly what leads to the false conclusion that “if props don’t change (i.e. memoized), then it will prevent the component from re-render”.

Because there is another very important reason for a component to re-render: when its parent re-renders itself. Or, if we go from the opposite direction: when a component re-renders itself, it also re-renders all of its children. Take a look at this code for example:

```tsx
const App = () => {
  const [state, setState] = useState(1);

  return (
    <div className="App">
      <button onClick={() => setState(state + 1)}> click to re-render {state}</button>
      <br />
      <Page />
    </div>
  );
};
```

`App` component has some state and some children, including `Page` component. What will happen when a button is clicked here? State will change, it will trigger App's re-render, and that will trigger re-render of all of its children, including `Page` component. It doesn’t even have props!

Now, inside of this `Page` component, if we have some children as well:

```tsx
const Page = () => <Item />;
```

Completely empty, it doesn’t have neither state nor props. But its re-render will be triggered when `App` re-renders, and as a result, it will trigger the re-render of its `Item` child. `App` component state change triggers a chain of re-renders across the entire app. See the <a href="https://codesandbox.io/s/parent-children-re-renders-gihtcw?file=/src/App.tsx">full example in this codesandbox</a>.

The only way to interrupt this chain is to memoize some of the components in it. We can do it either with `useMemo` hook, or, even better, with <a href="https://reactjs.org/docs/react-api.html#reactmemo">React.memo</a> util. _Only_ if the component is wrapped with it will React stop before re-rendering it and check, whether the props value changes.

Memoizing the component:

```tsx
const Page = () => <Item />;
const PageMemoized = React.memo(Page);
```

Using it in App with state change:

```tsx
const App = () => {
  const [state, setState] = useState(1);

  return (
    ... // same code as before
      <PageMemoized />
  );
};
```

In this, and **only** this scenario it’s important whether props are memoized or not.

To illustrate, let's assume that `Page` component has `onClick` prop that accepts a function. What will happen if I pass it to `Page` without memoizing it first?

```tsx
const App = () => {
  const [state, setState] = useState(1);
  const onClick = () => {
    console.log('Do something on click');
  };
  return (
    // page will re-render regardless of whether onClick is memoized or not
    <Page onClick={onClick} />
  );
};
```

`App` will re-render, React will find `Page` in its children, and will re-render it. Whether `onClick` is wrapped in useCallback or not is irrelevant.

And if I memoize `Page`?

```tsx
const PageMemoized = React.memo(Page);

const App = () => {
  const [state, setState] = useState(1);
  const onClick = () => {
    console.log('Do something on click');
  };
  return (
    // PageMemoized WILL re-render because onClick is not memoized
    <PageMemoized onClick={onClick} />
  );
};
```

`App` will re-render, React will find `PageMemoized` in its children, realise that it’s wrapped in `React.memo`, stop the chain of re-renders, and check first whether props on this component change. In this case, since `onClick` is a not memoized function, the result of props comparison will fail, and `PageMemoized` will re-render itself. Finally, some use for `useCallback`:

```tsx
const PageMemoized = React.memo(Page);

const App = () => {
  const [state, setState] = useState(1);
  const onClick = useCallback(() => {
    console.log('Do something on click');
  }, []);

  return (
    // PageMemoized will NOT re-render because onClick is memoized
    <PageMemoized onClick={onClick} />
  );
};
```

Now, when React stops on `PageMemoized` to check its props, `onClick` will stay the same, and `PageMemoized` will not be re-rendered.

What will happen if I add another non-memoized value to `PageMemoized`? Exactly the same scenario:

```tsx
const PageMemoized = React.memo(Page);

const App = () => {
  const [state, setState] = useState(1);
  const onClick = useCallback(() => {
    console.log('Do something on click');
  }, []);

  return (
    // page WILL re-render because value is not memoized
    <PageMemoized onClick={onClick} value={[1, 2, 3]} />
  );
};
```

React stops on `PageMemoized` to check its props, `onClick` will stay the same, but `value` will change, and `PageMemoized` will re-render itself. See the <a href="https://codesandbox.io/s/everything-memoized-8oltqg?file=/src/App.tsx">full example</a> here, try to remove memoization to see how everything starts re-rendering again.

Considering the above, there is only one scenario, when memoizing props on a component makes sense: **when every single prop and the component itself are memoized**. Everything else is just a waste of memory and unnecessarily complicates your code.

Feel free to remove all `useMemo` and `useCallbacks` from the code if:

- they passed as attributes, directly or through a chain of dependencies, to DOM elements
- they passed as props, directly or through a chain of dependencies, to a component that is not memoized
- they passed as props, directly or through a chain of dependencies, to a component with at least one prop not memoized

**Why remove, not just fix memoization?** Well, if you had performance problems because of re-renders in that area, you would’ve noticed and fixed it already, isn’t it? 😉 And since there is no performance problem, there is no need to fix it. Removing useless `useMemo` and `useCallback` will simplify the code and speed up initial render a bit, without negatively impacting existing re-renders performance.

## Avoiding expensive calculations on every render

The primary goal of useMemo, according to <a href="https://reactjs.org/docs/hooks-reference.html#usememo">React docs</a>, is to avoid expensive calculations on every render. No hints though of what constitutes the “expensive” calculation. As a result, developers sometimes wrap in `useMemo` pretty much every calculation in the render function. Create a new date? Filter, map or sort an array? Create an object? `useMemo` for all!

Okay, let’s take a look at some numbers. Imagine we have an array of countries (~250 of them), and we want to render them on the screen and allow users to sort them.

```tsx
const List = ({ countries }) => {
  // sorting list of countries here
  const sortedCountries = orderBy(countries, 'name', sort);

  return (
    <>
      {sortedCountries.map((country) => (
        <Item country={country} key={country.id} />
      ))}
    </>
  );
};
```

The question is: is sorting an array of 250 elements an expensive operation? Feels like it, isn’t it? We probably should wrap it in `useMemo` to avoid re-calculating it on every re-render, right? Well, easy to measure:

```tsx
const List = ({ countries }) => {
  const before = performance.now();

  const sortedCountries = orderBy(countries, 'name', sort);

  // this is the number we're after
  const after = performance.now() - before;

  return (
    // same
  )
};
```

The end result? Without memoization, with 6x CPU slowdown, sorting of this array with ~250 items takes **less than 2 milliseconds**. To compare, rendering this list - just native buttons with text - takes more than 20 milliseconds. 10 times more! <a href="https://codesandbox.io/s/measure-without-memo-tnhggk?file=/src/page.tsx">See the codesandbox.</a>

And in real life, the array likely will be much smaller, and whatever is rendered much more complicated, and therefore slower. So the difference in performance will be even bigger than 10 times.

Instead of memoizing the array operation, we should memoize the actual most expensive calculation here - re-rendering and updating components. Something like this:

```tsx
const List = ({ countries }) => {
  const content = useMemo(() => {
    const sortedCountries = orderBy(countries, 'name', sort);

    return sortedCountries.map((country) => <Item country={country} key={country.id} />);
  }, [countries, sort]);

  return content;
};
```

That `useMemo` drops unnecessary re-renders time of the entire component from ~20ms to less than 2ms.

Considering the above, this is the rule about memoizing “expensive” operations that I want to introduce: unless you actually calculating factorials of big numbers, remove `useMemo` hook on all pure javascript operations. Re-rendering children will always be your bottleneck. Use useMemo only to memoize heavy parts of the render tree.

**Why remove though?** Wouldn’t it be better to just memoize everything? Wouldn’t it be a compound effect that degrades performance if we just remove them all? One millisecond here, 2 there, and soon our app is not as fast as it could be…

Fair point. And that thinking would be 100% valid, if it wasn’t for one caveat: memoization doesn’t come for free. If we’re using `useMemo`, during the initial render React needs to cache the result value - that takes time. Yes, it will be tiny, in our app above memoizing those sorted countries takes less than a millisecond. But! This will be the true compound effect. The initial render happens when your app first appears on the screen. **Every** component that is supposed to show up goes through it. In a big app with hundreds of components, even if a third of those memoize something, that could result in 10, 20, at worst maybe even 100 milliseconds added to the initial render.

Re-render, on the other hand, only happens after something in one part of the app changes. And in a well-architectured app only this particular small part going to be re-rendered, not the entire app. How many of the “calculations” similar to the case above we’ll have in that changed part? 2-3? Let’s say 5. Each memoization will save us less than 2 milliseconds, i.e. overall less than 10 milliseconds. 10 milliseconds that may or may not happen (depends on whether the event that triggers it happens), that are not visible with the naked eye, and that will be lost in children’s re-renders that will take 10 times that much anyway. At the cost of slowing down the initial render that will always happen 😔.

## Enough for today

That was quite a lot of information to process, hope you found it useful and are now eager to review your apps and get rid of all the useless `useMemo` and `useCallback` that accidentally took over your code. Quick summary to solidify the knowledge before you go:

- `useCallback` and `useMemo` are hooks that are useful only for consecutive renders (i.e. re-renders), for the initial render they are actually harmful
- `useCallback` and `useMemo` for props don’t prevent re-renders by themselves. Only when every single prop and the component itself are memoized, then re-renders can be prevented. One single mistake and it all falls apart and makes those hooks useless. Remove them if you find them.
- Remove `useMemo` around “native” javascript operations - compare to components updates those are invisible and just take additional memory and valuable time during the initial render

One small thing: considering how complicated and fragile all of this is, `useMemo` and `useCallback` for performance optimisations really should be your last resort. Try other performance optimisation techniques first. Take a look at those articles that describe some of those:

* <a href="https://www.developerway.com/posts/how-to-write-performant-react-code">How to write performant React code: rules, patterns, do's and don'ts</a>
* <a href="https://www.developerway.com/posts/why-custom-react-hooks-could-destroy-your-app-performance">Why custom react hooks could destroy your app performance</a>
* <a href="https://www.developerway.com/posts/how-to-write-performant-react-apps-with-context">How to write performant React apps with Context</a>
* <a href="https://www.developerway.com/posts/react-key-attribute">React key attribute: best practices for performant lists</a>
* <a href="https://www.developerway.com/posts/components-composition-how-to-get-it-right">React components composition: how to get it right</a>.

And of course, goes without saying: measure first!

May this day be your last day in `useMemo` and `useCallback` hell! ✌🏼

...

Originally published at [https://www.developerway.com](https://www.developerway.com). The website has more articles like this 😉

[Subscribe to the newsletter](https://www.developerway.com), [connect on LinkedIn](https://www.linkedin.com/in/adevnadia/) or [follow on Twitter](https://twitter.com/adevnadia) to get notified as soon as the next article comes out.