React: memo, useMemo, useCallback and how to use it to boost your app performance

Yury Kozyrev
3 min readSep 14, 2021

--

Since React has introduced and started to advocate the tendency to use Functional Components, some developers have decided that they work with magic and will improve your app performance and predictability out of the box.

But Functional components make my app faster, don’t they?

Yes and no. Yes, it could be easier to implement isolation layers and render fences to prevent underlying components from rendering, and no: when you don’t know what it means or how to implement it.

Today I will try to explain how it works and will show that there is no magic in it. I will provide a step-by-step guide with examples of React.memo, and hooks like React.useContext, React.useMemo (and useCallback as well) usage.

Let’s go!

React.memo

In this article, this is the only function (HOC) that can work with both: class components and functional components. It was introduced in 2019 as a replacement forReact.PureComponent that has only one job: compare props and avoid rendering when they don’t change. Additionally, you can provide a custom comparator as a second argument.

You can read more about it in this article and in the official documentation which nowadays even has code examples.

So, how and when to actually use React.memo to wrap a component?

  1. No props. If your component is completely isolated and does not have any props at all — this is your obvious candidate. You can wrap its export with React.memo and it will be an efficient and fast rendering fence (an isolation layer that prevents underlying code/components from execution) and your component will re-render only when its internal state has changed.
  2. The amount of properties is relatively small. Sometimes it could be faster to render a component than to compare a long list of props. There is no golden rule, you will need performance testing to identify what option works faster.
  3. Rarely changing props of primitive types. When you pass string, number, or boolean props it’s easy to compare and hard to get into the situation when you accidentally provide a new variable (like object, array or a function) every time.

If you do pass an object though, an array, or a function you still can use React.memo, but you need to make sure your variables are changing only when needed, not on every render of the parent component. To secure that with Functional Components we have 2 friends: useCallback and useMemo.

useCallback and useMemo

First, I’d like to note that they use the same implementation, the only difference is that useMemo returns a result of a function you pass into it, and useCallback returns a function itself. And since they are essentially the same, I will be referring to useMemo.

When your component is rendered, every line of code is executed on every render. So if you define in your component function, object, or a list, every time it will be a new function, object, or a list. Sounds like a waste of computing resources, doesn’t it?

But not only that! Every time when you pass a new function to your child component that you carefully wrapped with React.memo after the previous chapter, your child component will be re-rendered. Which makes sense.

There is no magic, have a look at useMemo source code.

So, when you want to eliminate this waste of resources to generate a dozen of objects and functions just for one-time use you can wrap them with useMemo to ensure they will change only when one of the specified dependencies was updated.

This observation leads to the rule:

Whenever you want to make a pure functional component, you need to:

  • Wrap it with React.memo
  • Ensure all props are either primitive or wrapped with useCallback/useMemo

Having only one of the two points accomplished, you most likely will not gain measurable performance improvement.

--

--

Yury Kozyrev

Former Yandex Software Engineer, passionate Engineering Manager in Berlin