loading...
Cover image for React.memo(use me wisely)

React.memo(use me wisely)

assuncaocharles profile image Charles Assunção ・3 min read

React.memo is fairly simple but misunderstood High Order Component. By the docs we get this definition:

If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.

performance boost 🤩? WOW, so let's memoize everything!

⚠️ Calm down Sherlock, use it wisely.

Let's have a deep look at what is the magic happening inside memo first when I said it is simple, you will have to agree after looking into the code in the react repo. The entire code is basically this:

const REACT_MEMO_TYPE = Symbol.for('react.memo');

export function memo<Props>(
  type,
  compare?: (oldProps: Props, newProps: Props) => boolean
) {
  const elementType = {
    $$typeof: REACT_MEMO_TYPE,
    type,
    compare: compare === undefined ? null : compare,
  };

  return elementType;
}

So this means that when you use it passing SomeComponent as to it you get as return basically:

  {
    $$typeof: Symbol.for('react.memo'),
    type: SomeComponent,
    compare: compare || null,
  }

This means that if you want to use this directly you will get the same result, don't believe me? Try it out in this codesandbox.

You can notice that there's a second parameter for memo, a compare function. Let's look into the docs again:

By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.

So this means that we can have the power to decide if the component should re-render even if the props might change. So let's say you never want your component to re-render again after the first time ( for whatever the reason you want to do that ), you can simply do this:

const MemoComponent = React.memo(({ name }) => {
  return <div>{name}</div>;
}, () => true);

⚠️ Notice that I am returning true in the compare function, I am basically saying that the meaningful previously props are the same as the next props so it should not re-render. ( It's basically the inverse of the shouldComponentUpdate )

So when to use it properly? Mainly you want to use it when your component is a pure functional component and/or it will always render with the same props and when you want to have control about if it should render or not. So, for example, imagine you have the following component:

const UserInfoHeader = ({avatar, name}) => (
    <>
        <img src={avatar} />
        <div>Hi {name}</div>
    </>
)

And you gonna probably use it inside some dashboard, this is perfect opportunity to use memo, your dashboard may rerender and change several internal states but very unlikely to change the name or the avatar source, so wrapping the UserInfoHeader with React.memo will avoid unnecessary renders here.

We have some better understanding now, but what is the problem if we wrap everything with memo? When it should be avoided?

Using memo unwisely can lead you to bugs hard to debug and to the false feeling to be improving the performance. The question then is when NOT to use React.memo?

1. Often props change

React.memo has the cost to compare the props every render and if your components are updating the props all the time you are likely to lose performance since normal components wouldn't care about it and would just re-render normally.

2. Compare function is too expensive

Let's say you have a complex object being passed as prop and you want to make a very expensive calculation comparing the previous object with the new one. Heads up here, you might be also decreasing the performance than simple just re-rendering always.

3. Components with that receive functions as props

This is not a don't but a be careful when using callbacks in memoized components, for example:

const Input = React.memo(...)

const App = () => (<Input onChange={(e) => { console.log(e) }} />)

In this case, the Input is going to be re-render all the time anyway because in each App render we will be redefining the onChange for the Input so either use static functions here or combine it with useCallback to prevent it to happen.


Wrapping up

React.memo is a powerful and easy to use HOC to gain performance but the misusage can backfire and actually harm the performance. Try to always be careful with it and use some profiling tools to make sure about the valid use cases.

Do you have an example of good usage for memo or have you faced some bug because of it? Share with us in the comments. :)

Posted on by:

Discussion

pic
Editor guide