React: Q: When should you NOT use React memo?

Created on 19 Dec 2018  Ā·  34Comments  Ā·  Source: facebook/react

I've been playing around with React 16.6.0 recently and I love the idea of React Memo, but I've been unable to find anything regarding scenarios best suited to implement it. The React docs (https://reactjs.org/docs/react-api.html#reactmemo) don't seem to suggest any implications from just throwing it on all of your functional components. Because it does a shallow comparison to figure out if it needs to re-render, is there ever going to be a situation that negatively impacts performance?

And second question: as long as everything remains pure, is there ever a situation to not wrap a functional component with React Memo?

Thank you.

Hooks Question

Most helpful comment

Can someone please explain (like I'm 5) WHY it's a bad idea to wrap all components in React.memo? If the props on a component haven't changed then it shouldn't re-render as it will simply be returning the exact same component as the initial render.

I also don't understand why the react docs say Do not rely on it to ā€œpreventā€ a render, as this can lead to bugs. What bugs? Why else would you use React.memo if not prevent an unnecessary re-render?

For example, on my main index page I include a <Header> component that takes in title and metaDescription as props. These props never change for the page so the Header component shouldn't re-render whenever something else is changed on the index page (e.g. someone clicks a button that changes state). It seems the only way to do this is by wrapping the Header component in React.memo, but apparently the docs imply this is the incorrect use.

All 34 comments

I would assume that the same general advice applies for React.memo as it does for shouldComponentUpdate and PureComponent: doing comparisons does have a small cost, and there's scenarios where a component would _never_ memoize properly (especially if it makes use of props.children). So, don't just automatically wrap everything everywhere. See how your app behaves in production mode, use React's profiling builds and the DevTools profiler to see where bottlenecks are, and strategically use these tools to optimize parts of the component tree that will actually benefit from these optimizations.

I would also imagine it'd apply to the useMemo hook.

The main thing I'm interested in relating to this question is about the cost of class initialisation (and similar) - It's something that I see just dropped in in couple of places of the React docs by Facebook, without any evidence or references. I'm not challenging Facebook, but I'd love to read more about it, so I can know what they clearly do, and thus optimize my builds.

They do the same with hooks, where they state:

For example, classes donā€™t minify very well, and they make hot reloading flaky and unreliable.

Which is the first I've heard of classes messing with hot reloading - in my experience I've not actually ever had a working hot reloading system that actually does swap out small chunks of code. That could be b/c I use classes everywhere, but I've never in my readings come across anything saying 'classes are screwing up my hot reloading' or 'hey classes cost you ~5kb more than functional components'.

I think a big factor in the answer on when to use memo relates to how/when React actually does component mounting/initialization. I know that information is on hand in the React docs (and other places), but it's not really mentioned "around" memo talks, making it really easy to forget.

From what I remember, it's something like " does not create a whole new element everytime - thats why mount methods & constructors don't get called every render. Instead, React does some magic to somehow re-use or compare the instance if it exists". This could be completely wrong or muddled, but that's also kind of my point.

I'm not trying to derail this issue at all (And I do apologize for my ramblings in this comment), but I do feel it'd be nice to have some clear cut examples and discussion about where it's costing you (and saving you) to memo. It'd especially be cool to have some extreme examples, with really slow components that are "magically" speed up with memo šŸ˜

useMemo has a different use case than React.memo / shouldComponentUpdate / PureComponent. It's not about preventing an entire component from re-rendering, it's about simply memoizing some data. Very unrelated in usage. The only similarity is that React.memo() and useMemo() both involve "if you see the same inputs as last time, don't do any extra work - return what you had before", but React.memo is for wrapping up entire components, and useMemo() is for whatever you want to return from the callback.

I'm not sure what "class initialization" has to do with React.memo. If I wrap a class component in React.memo and render <MemoizedClassComponent />, the class instance gets created either way. The difference is that there's an invisible extra layer in the component tree, and it won't re-render as often depending on the incoming props.

Classes definitely make hot reloading more difficult, because of trying to swap out prototypes and stuff. I'm not clear on all the nitty-gritty details, but look through the https://github.com/gaearon/react-hot-loader repo to see all the complexities involved.

Overall, the React team has frequently emphasized on social media that "wrapping everything in a PureComponent is a bad idea", but I agree it'd be nice to actually have that in the docs.

As far as I know, everything in https://reactjs.org/docs/optimizing-performance.html that relates to shouldComponentUpdate and PureComponent applies to React.memo as well.

You might also want to read some of the articles on optimizing React performance that I have listed at https://github.com/markerikson/react-redux-links/blob/master/react-performance.md .

useMemo has a different use case than React.memo / shouldComponentUpdate / PureComponent. It's not about preventing an entire component from re-rendering, it's about simply memoizing some data. Very unrelated in usage. The only similarity is that React.memo() and useMemo() both involve "if you see the same inputs as last time, don't do any extra work - return what you had before", but React.memo is for wrapping up entire components, and useMemo() is for whatever you want to return from the callback.

You're completely right - that was me being stupid. I've been working too hard lately, and it's left my brain in a muddle šŸ˜¬

I'm not sure what "class initialization" has to do with React.memo

(I actually technically mean instancing - sorry about the confusion)

In the hooks FAQ, they have the question Are Hooks slow because of creating functions in render?
, which they state in the answer:

Hooks avoid a lot of the overhead that classes require, like the cost of creating class instances and binding event handlers in the constructor.

For Facebook to say "the cost of creating class instances" implies to me there is a cost worth mentioning (i.e, compared to the cost of doing something like const array = [], which doestechnicallyy have a cost, but it's so insanely small no one ever talks about it), but in all my readings about props & cons of classes in JS, I've never come across anyone arguing that classes have a 'cost' worth mentioning (which I figured people would jump on, as ES6 classes were a hot topic of debate back when they were introduced).

I'm assuming that the cost is actually one React suffers with classes due to how it works, where the cost is only a factor when you're creating lots of class instances in the space of seconds all the time, but again, this isn't something I recall seeing explored or talked about in detail in the rest of the React documents.

Thanks for the links - I've not encountered them before, despite my best efforts šŸ˜• You've given me some good reading to do!

In React 15 and earlier, function components were wrapped in classes internally, so there was no real difference in perf behavior.

In React 16, function components are now handled separately, with no class wrapping them. This means there's less overhead to rendering a function component overall than for a class component. References:

Dominic Gannaway:

Functional components should be slightly faster in React 16 as there's no instance created to wrap them (unlike React 15).

Andrew Clark:

Think of all the features class components have that we can totally skip with functional components. Constructors, refs, lifecycles, error handling. The only reason they were the same performance-wise before Fiber was technical debt in the the Stack implementation.

So, in general, an app that consistently renders N function components should take less total time to render than an app that renders N class components. How _much_ less, I have no idea - you'd have to benchmark things.

However, all of that is mostly separate from the original question of "when should I use React.memo()?"

I understood what you meant by "class initialization" - the cost of creating a single instance of a given class component, because you're rendering it for the first time. My point is that that has nothing to do with use of React.memo, because rendering <PlainClassComponent> for the first time has basically the same cost as rendering <MemoWrappedClassComponent> for the first time. In both cases, a class component instance is created.

Also, that hooks FAQ entry is largely there because of the longstanding fear and confusion in the React community over "creating functions in render being bad for performance". See Ryan Florence's post React, Inline Functions, and Performance for thoughts on that topic.

Can someone please explain (like I'm 5) WHY it's a bad idea to wrap all components in React.memo? If the props on a component haven't changed then it shouldn't re-render as it will simply be returning the exact same component as the initial render.

I also don't understand why the react docs say Do not rely on it to ā€œpreventā€ a render, as this can lead to bugs. What bugs? Why else would you use React.memo if not prevent an unnecessary re-render?

For example, on my main index page I include a <Header> component that takes in title and metaDescription as props. These props never change for the page so the Header component shouldn't re-render whenever something else is changed on the index page (e.g. someone clicks a button that changes state). It seems the only way to do this is by wrapping the Header component in React.memo, but apparently the docs imply this is the incorrect use.

@markerikson -

So, in general, an app that consistently renders N function components should take less total time to render than an app that renders N class components. How much less, I have no idea - you'd have to benchmark things.

Dramatically, it seems.

Here's the best info I've found on this. Seems like there are few cases where you _wouldn't_ want to use React.memo, theoretically. Though the significant extra typing in every component seems clunky.

Perhaps your parent component rerenders regularly and your child component does something impure like render the current time or fetch some data... though I'd think hooks would cover both of those well.

Regarding when to use React.memo(), I wrote an interesting blog post. Check it out.

I use the following rule of thumb:

The more often the pure functional component re-renders with the same props, the heavier and the more computationally expensive the output is, the more chances are that component needs to be wrapped in React.memo()

I always use PureComponent for class based components. It's easy, and it just works. I've noticed that if I don't wrap basically all my components in memo(), nothing gets optimized. And since children are part of props, I had a rouge non-memoized component that cause everything to re-render().

I'm trying to like hooks, and some of them are useful, but the mental overhead is actually large. Ract.memo(), useCallback(), useState(), useRef(), useMemo(), useContext(), all used together in a < 100 line fine gets a bit hard to read. An example I've created to optimize dragging, which has a hooks version and a class version:

https://github.com/mikeaustin/optimized-drag/tree/master/src

Itā€™d be great if there was a helper to let you diff everything except for children, and have React handle diffing Children.

If you really, really want it... You could technically put the children into useMemo...

import React, { useMemo } from 'react';

const Foo = React.memo(({ children }) => {
  return children;
});

const Hello = () => {
  const childrenTest = useMemo(() => {
    return (
      ...children here...
    );
  }, [...to watch for changes, call it AAA...]);

  return (
      <Foo {...AAA}>{childrenTest}</Foo>
  );
}

It's just going to pollute that Foo component with a bunch of props that it may not need (since you also need to pass in all the children props into Foo).

Let us say that we have a case like this one:

const MyComponent = React.memo(({genre, name}) => {
    const someFunction = genre => {
        return (genre === 'male') ? 'Mr.' : 'Ms.';
    }

    return (
        <h1>Hello {someFunction(genre)} {name}!</h1>
    );
});

Does it worth to create a memoized functional component when it has an additional function? Does that additional function also get memoized, or in that case I would be slowing down things by creating a new function on every render? In this example, would be better to move on to a class-based component?

Let us say that we have a case like this one:

const MyComponent = React.memo(({genre, name}) => {
    const someFunction = genre => {
        return (genre === 'male') ? 'Mr.' : 'Ms.';
    }

    return (
        <h1>Hello {someFunction(genre)} {name}!</h1>
    );
});

Does it worth to create a memoized functional component when it has an additional function? Does that additional function also get memoized, or in that case I would be slowing down things by creating a new function on every render? In this example, would be better to move on to a class-based component?

I think you need to use 'useCallback' to memoize the extra function, pajarito.

@Bohmaster I think he is not using someFunction as a callback to the child component. useMemo should work here.

What do you think ?

@syntaxbliss React.memo will skip re-rendering the component for same props and instead take the previous memoized ReactElement "snapshot". It's like React does not call the MyComponent function again for re-render, but take its last result.

Perhaps a bit more clear statement is: the resulting ReactElement/JSX element, to which your inner someFunction function contributed something, is memoized.

I would not care about function creation cost in render for someFunction. Also you pass this inline function only to a primitive DOM node, so there is no need to ask yourself, if memoization gets broken due to creating a new function reference each render.

Maybe this answer also helps to decide, if React.memo is worth it for your case.

Can someone please explain (like I'm 5) WHY it's a bad idea to wrap all components in React.memo? If the props on a component haven't changed then it shouldn't re-render as it will simply be returning the exact same component as the initial render.

I'm just guessing based on what I know, but since not everyone using React embraces immutability, there might be cases where someone passes data into a non-memoised ChildComponent, then mutates that data (an object or array) and expects that component to re-render, ie. because its parent did.

As soon as you memoise that ChildComponent using React.memo, the mutated data won't trigger a re-render because of referential equality, even though its parent did re-render.

The other potential downside of React.memo is the work that goes into shallow comparison, even though for most apps that's probably negligable.

@StoneCypher @fbartho @mikeaustin @marcelkalveram @gaearon @rlesniak @sunny @panzerdp

You should always use React.memo LITERALLY, as comparing the tree returned by the Component is always more expensive than comparing a pair of props properties)

So don't listen to anyone and wrap ALL functional components in React.memo. React.memo was originally intended to be built into the core of functional components, but it is not used by default due to the loss of backward compatibility. (Since it compares the object superficially, and you MAYBE in the component, use the nested properties of the sub-object) =)

That's it, this is the ONLY REASON why React doesn't use memo Automatically. =)

In fact, they could make version 17.0.0, which would BREAK backward compatibility, and make React.memo the default, and make some kind of function to CANCEL this behavior, for example React.deepProps =)

Stop listening to theorists, guys =) The rule is simple:

If your component USES DEEP COMPARING PROPS then don't use memo, otherwise ALWAYS USE, comparing TWO OBJECTS is ALWAYS CHEAPER than calling React.createElement () and comparing two trees, creating FiberNodes, and so on.

Theorists talk about what they themselves do not know, they have not analyzed the react code, not understand FRP, and do not understand what they advise =)

Always yours, one of the react developers (_Now working at AWS =) ahah_)

Everyone please ^ __ ^

image

@StoneCypher and @mikeaustin

Prove your arguments, or the whole community will see that you, you could not win the PUBLIC COURT, and cowardly put dislikes. WHAT EXACTLY arguments COULD you counter my arguments?

WE as the whole React community listen to your counterarguments =))

Let's laugh right now =) we listen to your arguments

image

Please stop tagging me

@StoneCypher @fbartho @mikeaustin @marcelkalveram @gaearon @rlesniak @sunny @panzerdp

But I want to teach you the truth)! I don't want you to live in misconceptions about React.memo, why don't I hear gratitude, but get dislikes? I sat, wrote, explained, tried, did you all GOOD. And he did not A SINGLE EVIL. And you are NOT GRATEFUL to me FOR GOOD, but you are doing EVIL: put dislikes. Why are you FOR GOOD - answer with EVIL? are you just -- EVIL????

image

Please stop tagging me too, I was never in the conversation in the first place.

@sunny strange, apparently this is a GitHub bug

@MaxmaxmaximusAWS please stop tagging people in this thread.

@richardjrossiii Why shouldn't I be tagging people to whom I communicate? I've always used this GitHub feature like that and will continue to use it and no one will stop me. Except for having a REASON. What is the reason why I should not use the functionality of GutHub DIRECTLY? WHAT is this reason?

P.S. Pleact, stop tagging ME in this thread. Are you against tagging? Then why are you tagging me? Are you against tagging people? I am people. Logic is a valuable thing, not everyone has it.

Usually not very smart people ignore the important question in the middle of the line and answer only at the end of the line. Therefore, I will repeat the question to you:

Why shouldn't I use the tagging functionality for its intended purpose? What reason? And if Github is against tagging, why does it then CREATED this feature?

To be honest, I would actually like to hear another opinion on what Mr. Maxmaxmaximus wrote in his first message (all trolling and jokes aside). It actually makes sense that comparing couple of props shallowly should consume less resources than executing the render function with all the hooks in it and building the JSX for the component (and all the sub-components). Strong/strict comparison (===) is a very simple operation.

Also, could you explain to me, will React always compare entire virtual DOM trees or only the parts that actually changed? So if I exclude the part of the virtual DOM tree using React.memo will it actually be excluded from reconciliation process? Or is it just skips the JSX generation? I guess the second, but I would like to hear a confirmation from someone more knowledgeable.

Thank you!

@sunny

I guess the second, but I would like to hear a confirmation from someone more knowledgeable.

1) React call component function
2) React looks what elementts and components returned from function
3) React create FiberNodes for all returned elements and components
4) React call fiberNode.execute() for all created fiberNodes
5) What returned from fiberNode.execute() function, react will "substitute" this for the fiber node
6) FiberNode.execute() has optimisation. if you use React.memo() then fiberNode.execute() returns previous result (fibers nodes thee) and not call fiberNode.execute() of them.
7) React.memo memorized ALL child breanch of tree, you do not need to memorize each child individually

No trolling as you requested =)

Again, please stop mentioning meā€¦

Considering that several people have asked you to stop tagging them and that your comments have been tagged as disruptive, I'd wager you are the one with inappropriate, irrational behavior.

If you receive notifications, turn them off in the settings

I've blocked you. This is the first time in 12 years of GitHub that've had to do thatā€¦ Please listen to what people are telling you and act more professionally.

I want to point out that from my experience, people often fall into one of the three categories:

  1. people who don't have an idea of what memo is
  2. people who use memo almost everywhere
  3. people who try adding it in places that are expected to benefit from it

All of them usually forget to memorize some super important component here and there (the website's Header is a classic example) or memoize a component and pass it an always invalidated prop like style={{width: '100%'}} so it renders again and again.

Sometimes these places render many components, have logic and side effects. Sometimes they are rendered very often.

I think the biggest benefit of making everything pure is "keeping it simple" by removing this feature altogether even though in some cases it would make the application a little slower.

@vzaidman React.memo actually have second argument for deep comparatiom. for this reason we have to use React.memo everywhere, even for style={{width: '100%'}}

function MyComponent(props) {
  /* ender using props */
}

function areEqual(prevProps, nextProps) {
  /*
    returns true if nextProps renders
    the same result as prevProps,
    otherwise returns false
  */

  return prevProps.style.width === nextProps.style.width
  // or like this:
  return JSON.stringify(prevProps.style) === JSON.stringify(nextProps.style)
}

export default React.memo(MyComponent, areEqual);

This would be an overkill in 99% of cases and also hard to make sure you will not get some kind of onClick={() => and then the whole nice style comparison is useless

People should use useCallback for callbacks, to become memo works.
Apollo and React native hooks, actually use useCallback. This is a problem for bad developers, and it should not drag us back, but it should push them forward. they should think "hmmm, but memo won't work for child components, if there is a memo, you shouldn't be lazy and wrap my callback in useCallback."

const [state, setState] = useState()

setState // it is actually memorized callback

if we use useEffect and setState inside, useEffect not triggeret every component render

const [state, setState] = useState()

useEffect(() => {
  setState(true)
}, [setState]) // this dependency never will not changed

in theory, our entire application should be wrapped in React.memo with correct props comparison settings, and use Ź»useCallback` for callbacks.

then the reactivity atom will be optimized as much as possible, and the react will render ONLY what could really change. our application is speeding up by using a lot of memory.

You can even write an extension in babel that automatically memorizes all callbacks, but I'm not a fan of "magic".

Due to violations of our code of conduct, we are putting a temporary block on the user @MaxmaxmaximusAWS for two weeks.

Was this page helpful?
0 / 5 - 0 ratings