176

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?

A situation like this seems like an obvious choice for implementation:

// NameComponent.js
import React from "react";
const NameComponent = ({ name }) => <div>{name}</div>;
export default React.memo(NameComponent);

// CountComponent.js
import React from "react";
const CountComponent = ({ count }) => <div>{count}</div>;
export default CountComponent;

// App.js
import React from "react";
import NameComponent from "./NameComponent";
import CountComponent from "./CountComponent";

class App extends Component {
  state = {
    name: "Keith",
    count: 0
  };

  handleClick = e => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <NameComponent name={this.state.name} />
        <CountComponent count={this.state.count} />
        <button onClick={this.handleClick}>Add Count</button>
      </div>
    );
  }
}

Because name will never change in this context, it makes sense to memoize.

But what about a situation where props change frequently?
What if I added another button that changed something else in the state and triggered a re-render, would it make sense to wrap CountComponent in memo, even though this component by design is meant to update frequently?

I guess my main question is as long as everything remains pure, is there ever a situation to not wrap a functional component with React Memo?

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
Keith Brewster
  • 3,302
  • 2
  • 21
  • 28
  • 3
    If your component always re-renders, it will do an unnecessary shallow prop check every time. It's the same as PureComponent. – Andy Ray Oct 31 '18 at 00:24
  • @AndyRay I guess I'm interested in where the threshold for diminishing returns lies. If my functional component theoretically needs to re-render exactly 95% of the time, will the cost of that one re-render necessitate the use of memo? I know that's a super specific scenario and I'm not looking for exact benchmarks, I was just more curious if there's a line that can be drawn in the sand. – Keith Brewster Oct 31 '18 at 00:34
  • 1
    I doubt anyone has worked on that metric, and if memoization calls are your bottleneck, there's probably something very specific to your app needs that's hard to give general advice for. – Andy Ray Oct 31 '18 at 00:39
  • 1
    Maybe a more poignant question would be: "When should you NOT use React memo?". Or "Should you ALWAYS use React memo and opt out only if there are perf problems?". – protoEvangelion Nov 03 '18 at 18:30
  • 1
    @protoEvangelion great suggestion, I'm going to update the question title – Keith Brewster Nov 04 '18 at 18:11

10 Answers10

92

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 are using the nested properties of the sub-object in the component) =)

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 it, 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, they do not understand FRP and they do not understand what they're advising =)

P.S. if your component is using children prop, React.memo will not work, because children prop always makes a new array. But It is better not to bother about this, and even such components should ALSO be wrapped in React.memo, since the computing resources are negligible.

Jalal
  • 334
  • 1
  • 4
  • 16
  • 48
    Any refutation on this? – anonym Aug 24 '20 at 03:13
  • 3
    @anonym think of React.memo as useEffect (Component, [depending ...]) Only in the case of useEffect, the documentation initially stated that the array values are compared superficially =), and in the case of props, this was not stated, this is the reason why React.memo is not enabled by default =) I am one of the developers of react, by the way. – Maxmaxmaximus Aug 31 '20 at 10:04
  • 2
    @anonym We had an idea that React.memo would not stop rendering of its children, but that the children would also be checked for props changes. Now React.memo blocks the rendering of its entire tree branch, this is also one of the reasons why it is not enabled by default. The trick is that reactivity has such a concept as a "grain of reactivity"; each grain has dependencies. The dependencies are compared, and if they have not changed, the grain is not recalculated. These are standard optimizations in FRP (React is one of the FRP implementations) – Maxmaxmaximus Aug 31 '20 at 10:12
  • @anonym Although I haven't looked at the react code since the addition of fibers, I need to see if memo blocks the re-checking of child components' dependencies (props). But the essence does not change much. Reactivity is "imperative declarative" =), these are chunks of imperative code that are re executed when their dependencies change. from here and the word `activation` and `REactivation` (when the dependencies changed) =), in general, React.memo has to be written manually for now (later I will write the babel plugin) – Maxmaxmaximus Aug 31 '20 at 10:20
  • 4
    @anonym I believe the only caveat is in case your props are memory-heavy. Memoizing means storing props in memory potentially forever, i.e. until the next rerender, where new potentially memory-heavy props need to be stored. Only when the component is unmounted, that memory will get freed. That's the trade-off you get between speed and memory. – guitarino Apr 15 '21 at 03:13
  • 2
    Anyone knows of a babel plugin that would automatically wrap all function components with React.memo? – Martin Kadlec Jun 11 '21 at 16:30
  • 1
    @guitarino That's true to a degree, but the comparison of props with useMemo is shallow where as the VDOM diffing is deep. So even if you have a large array or object in props it will only check that the top level object is the same or not. This means it's not simply a size comparison between the two you need to account for. – TheZanke Nov 29 '21 at 15:46
  • 8
    @IvanKleshnin it doesn't depend on the size of props (memory) - it depends on the number of props. If I had to guess, situations where there there are a lot of props (more than vdom nodes + attributes), are exceedingly rare. I'm not sure if I've ever encountered a reasonable situation like that. Most components I've ever worked with, have few props. Not to mention, components may have more custom logic (hooks, effects, state recalculation, etc) that may be called unless `React.memo` is used. – guitarino Dec 05 '21 at 00:13
  • 2
    @IvanKleshnin Your answer sounds reasonable, but not. A coupon of reasons: - `React.memo` has a comparer(the second parameter)。That means it could always be faster than vdom comparison. Of course, it needs extra code. - If there're a lot of props, the calculation should be complex in render function(may be you will say there's useMemo for this, but most of time,`useMemo` is used for optimizing state change). - Before vdom comparison, vdom creation is also needed, which has extra cost. - Most importantly, `React.memo` can avoid subsequent calculations. – Youxiang Lu May 10 '22 at 11:50
  • Can you please add some examples for "Since it compares the object superficially, and you MAYBE are using the nested properties of the sub-object in the component"? – Ini Jun 20 '22 at 10:10
  • 1
    Advising USE ALL THE TIME is just bad advice. There's no silver bullet anywhere in a world, so is not in React. Real world example - pass image in Base64 in props. Shallow compare will definitely hurt your performance. – OSP Jul 11 '22 at 07:04
  • If the component is re-rendered all the time because props are changing then the memo is slower... If you are passing in props some event handlers, then it will always re-render unless you use useCallback which has an additional cost on its own. If other properties change often, then you will add to the memory the component, properties, callbacks and all of this to fill more memory and gain nothing. – Adrian Stanisławski Jul 11 '22 at 12:10
  • What about memo exporting a 'propless' component ? Would it yield hindered performance ? @AdrianStanisławski – emre-ozgun Nov 03 '22 at 08:50
  • @emre-ozgun I suppose it depends on the component. If it renders a span with one line of static text then I do not think a memo is a good option as such render is extremely fast. But if it calculates something at the beginning then a memo is probably a good option. – Adrian Stanisławski Nov 04 '22 at 10:04
  • Kent C Dodds argues that it should be used sparingly because React is fast as is and it `React.memo` introduces complexity. https://kentcdodds.com/blog/usememo-and-usecallback#reactmemo-and-friends – Adam Zerner Nov 14 '22 at 03:23
  • I agree with @IvanKleshnin . Indeed the comparison depends on the size of the data. But from my experience, 90% of the time the tree of a component is much larger than its props, so using React.memo() everywhere will be worth it 90% of the time!!! Also, another reason that React.memo() is not been used by default, is because it would confuse newcomers a lot!!! They would definitely create bugs where components would not re-rendered and they would smash their heads before finding what they are doing wrong. Also I like to think React as the C++ of frontend. YOU are responsible for controlling it – babaliaris Dec 25 '22 at 17:55
  • Don't listen to this theorist who does not seem to understand a bit about the topic, and do NOT use `React.memo` mindlessly. Thank you. – Kostiantyn Ko Jan 27 '23 at 13:27
  • FWIW [this comment on the react issue discussing this topic](https://github.com/facebook/react/issues/14463#issuecomment-673844197) has double the amount of thumbs down as thumbs up, so take the advice here with a large pinch of salt. – fredrivett Feb 03 '23 at 17:43
  • This should works, but if you are lazy – Ali Akbar Azizi May 31 '23 at 04:33
  • From our experience `memo`, `useMemo`, and `useCallback` can optimize re-renders but slows down the first render. Sometimes memorization on every level in the components tree becomes too heavy and actually doesn't give benefits on re-render. Instead, for example, you can memo the item in the list that can hold a lot of items, but memo components inside of item may only slow down the render without real benefits – Dmitry Masley Aug 07 '23 at 10:19
87

All react components implement the shouldComponentUpdate() method. By default (components extending React.Component), this returns true, always. The change that memoizing a component (through React.memo for functional components or extending React.PureComponent for class components) introduces is an implementation of the shouldComponentUpdate() method - which does the shallow comparison of the state and props.

Looking at the documentation on the component lifecycle methods, shouldComponentUpdate() is always called before the render happens, this means that memoizing a component will include this additional shallow comparison on every update.

Taking this into consideration, memoizing a component does have performance effects, and the magnitude of these effects should be determined through profiling your application and determining whether it works better with or without the memoizing.

To answer your question, I don't think there's an explicit rule when you should or should not memoize components, however I think the same principle should be applied as when deciding whether or not you should override shouldComponentUpdate(): find performance issues through the suggested profiling tools and identify whether or not you need to optimise a component.


As of 2023, the official React documentation also discusses this, with similar advice: memo - Skipping re-rendering when props are unchanged. Note especially the discussion under "Should you add memo everywhere?":

Optimizing with memo is only valuable when your component re-renders often with the same exact props, and its re-rendering logic is expensive.

sleske
  • 81,358
  • 34
  • 189
  • 227
Ntokozo Zwane
  • 1,572
  • 14
  • 20
  • 3
    This is a great answer, and I think you're absolutely right that profiling is a necessary tool for determining the magnitude of performance pitfalls. – Keith Brewster Jun 10 '19 at 15:02
  • 1
    Yeah I think a lot of the features that React gives us are not meant to be magic wands, but more tools to help solve issues in certain (sometimes general) situations, I'm glad you found my answer useful @KeithBrewster – Ntokozo Zwane Jun 10 '19 at 21:06
  • 7
    You should have mentioned memory implications (memory is not a performance). I dunno why people think the cost of `React.memo` is just an extra shallow comparison. When it's actually "extra shallow comparison" + "extra render snapshot for each component instance". At least for functional components. – Ivan Kleshnin Jun 25 '19 at 08:53
  • I think you've mentioned that shouldComponentUpdate() does a shallow comparison of state and props. I think the comparison is only of the props and not the state. The state comparison is even before this method is called because if the state is the same you'll not get any callback in the shouldComponentUpdate() method. @NtokozoZwane – jarora Feb 11 '21 at 12:20
  • 3
    @IvanKleshnin interesting point! I'd be interested in an elaboration for the second statement "extra render snapshot for each component instance" – Nick Mitchell Jul 19 '21 at 01:29
  • 3
    @NickMitchell I meant `React.memo` internals store prop values in memory (to be able to compare with new prop values). If that data is big enough it can have a measureble footprint. – Ivan Kleshnin Jul 19 '21 at 12:59
  • Correct me if I'm wrong or misunderstand, but I believe React.Component doesn't implement shouldComponentUpdate(). Rather, only React.PureComponent implements shouldComponentUpdate(). Source: https://reactjs.org/docs/react-api.html#reactpurecomponent. – Brendan Aug 30 '21 at 23:54
  • **> I don't think there's an explicit rule when you should or should not memoize components** @NtokozoZwane I disagree. One rule is there. If your component renders the same result given the same props, you can wrap it in a call to `React.memo` for a performance boost. But if your component renders a different result given the same props, you should not use `React.memo` – ashuvssut Jan 22 '22 at 11:02
  • Added a link to the official docs, which support your points. – sleske Apr 26 '23 at 10:05
42

Is there ever going to be a situation that negatively impacts performance?

Yes. You may end up with worse performance, if all components are mindlessly wrapped by React.memo.

It is not needed in many cases. To give it a try with a performance critical component, do some measures first, add memoization and then measure again to see if added complexity was worth it.

What is the cost of React.memo?

A memoized component compares old with news props to decide, if to re-render - each render cycle.
A plain component does not care and just renders, after props/state change in a parent.

Take a look at React shallowEqual implementation, which is invoked in updateMemoComponent.

When NOT use React memo?

There are no hard rules. Things, that affect React.memo negatively:

  1. component often re-renders with props, that have changed anyway
  2. component is cheap to re-render
  3. comparison function is expensive to perform

Ad 1: In this case, React.memo cannot prevent a re-render, but had to do additional calculations.
Ad 2: Added comparison cost is not worth it for a "simple" component in terms of render, reconcile, DOM change and side-effect costs.
Ad 3: The more props, the more calculations. You also can pass in a more complex custom comparer.

When complement React.memo?

It only checks props, not context changes or state changes from inside. React.memo is also useless, if the memoized component has non-primitive children. useMemo can complement memo here, like:

// inside React.memo component
const ctxVal = useContext(MyContext); // context change normally trigger re-render
return useMemo(() => <Child />, [customDep]) // prevent re-render of children
ford04
  • 66,267
  • 20
  • 199
  • 171
  • 11
    How can re rendering a whole HTML component be any cheaper than literally comparing values, arguably one of the fastest operations in every programing language? this is wrong on so many levels. while its true that mindlessly memorizing components might literally take more cpu cycles, but there is no way it will ever compare to any re-render unless you literally compare thousands of parameters at once which is unlikely. – vasilevich Sep 06 '20 at 17:34
  • 6
    Well, my main point is: Given your component in most cases has changed props anyway in case of a parent re-render, `React.memo` has to do more work than a plain component (reconciling *and* props diffing). I personally am also not a fan of [premature optimization](https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578?gi=5f251596a04d#0ae8) - these changes always come with a cost (like the cost of added abstraction/complexity) and should be justified. – ford04 Sep 06 '20 at 18:31
  • 3
    Also interesting: in the linked article, [worse performance](https://cdb.reacttraining.com/react-inline-functions-and-performance-bdff784f5578?gi=5f251596a04d#f11e) has been experienced and measured, after having put `PureComponent` aka `React.memo` for functions on every component. – ford04 Sep 07 '20 at 08:35
  • 4
    You are correct in most of your points, and i agree with everything except for the cheapness of re-rendering. obviously on a component that is ALWAYS re-rendered, memorizing is useless, and is bad to performance. I just believe that the beautiful thing in react, and react-kind frameworks is the idea of a virtual dom which is main purpose to prevent unnecessary expensive real DOM manipulations. so that is kind of the point of the framework among other things. shallow comparison just doesn't fit every scenario but still a good idea for many scenarios. – vasilevich Sep 07 '20 at 12:19
16

The same question has an answer by markerikson on the React GitHub issue tracker. It got way more thumbs up than the answers here.

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.

andraaspar
  • 796
  • 6
  • 10
4

The idea is to avoid using memoization, for data which has the possibility of changing very often. As stated in the blog, this also includes callbacks which depends on such types of data. For example functions such as

<Foo onClick={() => handle(visitCount)}/>

I really enjoyed this simplistic read. The examples are great. https://dmitripavlutin.com/use-react-memo-wisely/

Zeena
  • 411
  • 4
  • 16
1

I think the short answer is: React.memo does to Functional Components what React.PureComponent does to Class Components. In that sense, when you use memo it will evaluate if the props to that functional component have changed, if so, then it will execute the return of the fuction, otherwise it will not, avoiding re-render of the component.

import React, { memo } from 'react';

const Component = () => {
  debugger;
  return (
    <div>Hello World!</div>
  );
};

const MemoComponent = memo(() => {
  debugger;
  return (
    <div>Hello World!</div>
  );
});

If you use Component as child component of a container that updates, everytime the parent updates it will re-render (debugger will trigger everytime). In the other hand if you use MemoComponent it will not re-render (debugger will only trigger in the first render).

In this example that happens because the functional component doesn't have props, in case it had props it will happen only if the props change.

Whymarrh
  • 13,139
  • 14
  • 57
  • 108
Michael
  • 288
  • 1
  • 7
  • 7
    This doesn't answer the question, which clearly asked when **not** to use memo, and not "what is memo", which is a different question, which you have answered. – vsync Aug 04 '19 at 14:31
  • 1
    @vsync I thought by explaining what it is, it would be clear enough to understand when to use or not (which at the end is the developer's choice). In my opinion, I think it should be used almost all the time, I can't think of any specific situation where I would suggest not to use it, honestly. – Michael Aug 05 '19 at 19:43
  • 1
    If indeed it should be used all the time and you cannot think of any use-case, then that should have been the default, and the smart people who authored React had chosen otherwise, which means there is a very good reason for it... [Here's a tweet](https://twitter.com/dan_abramov/status/1083897065263034368?lang=en) from Dan Abramov – vsync Aug 05 '19 at 20:27
  • @vsync I think you should read my comments again cause I think you are misunderstanding. First off, I said "Should be used almost all the time" (key word Almost). Secondly, when I say I can't think of a situation to suggest not to use it, well that's my personal and honest opinion, if you have a situation yourself then you should share it :) – Michael Aug 06 '19 at 21:23
  • Third, back to my original answer where I was trying to explain what it is and is not, that's the reason at the end it's the developers judgement whether or not to use it. When or not use lodash, for example... when use extend PureComponent instead of Component. Everything has a cost at the end, but in my opinion and personal experience I haven't had a cost that prevented me from using memo on my components vs the benefits of using it. – Michael Aug 06 '19 at 21:29
  • PS: I respect Dan A. but not just because he says something it's true, or not just because he emits his own opinion is the right thing. There's no absolute truths almost in anything. – Michael Aug 06 '19 at 21:31
  • I totally respect your effort in trying to answer the topic. for me, I came here from google, because I want to know when **not** to use `memo` and scrolling down the answers, there's no answer to the actual question. I am sure there are scenarios out there, so viewing this thread from a googling perspective, it's unhelpful, bottom line.. If I ever discover a scenario I will come back and post it here. – vsync Aug 07 '19 at 08:23
  • Closely related: [Why aren't all React components wrapped with React.memo() by default?](https://stackoverflow.com/questions/74013864/). – sleske Apr 26 '23 at 10:07
1

Configurable embedded test case

I set up a test case with some parameters to measure the average render time, both for the memo and non-memo version of otherwise the same component. You can try it by running the code snippet below. It doesn't serve as a definitive answer, rather it helps demonstrate how different circumstances affect performance.

If you only change the "with memo" checkbox, it preserves the average times so you can compare both. Changing any other setting will reset the counters.

Note there's a small constant amount of time added by the input elements, but it's quite stable and doesn't prevent you from observing the relative impact of parameters. Though feel free to adjust this answer if you can make it more reliable/accurate.

For the memo version it also keeps track of the average time for hits and misses separately. A hit means the memo was able to skip a render.

You can set how frequently the memo component needs to run (default every 10 renders).

Finally, assuming the change detection algorithm would stop as soon as any difference is detected, I added a parameter to reduce the amount of props being changed. You can turn it up to 100% to make it change only a single prop, or 0% (default) to change all props. The last prop is always changed. So far this didn't seem to result in any measurable change. You can probably just ignore this setting.

Limitations

The props are simple equally sized strings, which probably makes the comparison operation easier than it is in real world cases.

Some timing info is written in an effect as to not "disturb" React too much. As a result some stale time info stays displayed until overwritten.

The very first render is considerably slower, you'll have to run some renders afterwards to cancel it out.

Accuracy

This is not meant as an accurate measurement, rather a way to compare the behavior of the 2 variants under otherwise similar circumstances.

While the App component has some expensive logic, the time is only measured after that. I used an effect for stopping the timer, as it's pretty much the first thing React will do after it's done rendering, should be close enough for this purpose.

My observations

The results of my own testing confirm the current top voted answer. The impact of the additional check is miniscule, even for absurdly large amounts of props, and/or props that are large strings.

If you use memo on a component that changes literally every time (change interval of 1), it can only be slower by a certain amount. So there's a turnover point where you start to gain from it.

However, I found that even if there's a 1 in 2 chance the component will need to render, memo came out favorably.

In fact the impact of using memo is so small that it's hard to observe even with many / big props.

The time avoided by skipping rendering, on the other hand, is measurably increased even for simple components.

You can try it for yourself with different parameters, the check is always much cheaper than the work it avoids. I did not find any configuration where not using memo was faster on average... Or did I?

memo is slower on extremely large strings*

*If your component doesn't use the large string.

Just as I was about to submit my answer, I tried again increasing the strings to 1 million characters. For the first time memo was struggling, whereas the component was not.

enter image description here

Even with only 1 in 10 "misses" it's clearly slower on average.

But if you're passing strings of that size as props you probably have more than 1 performance problem, and this likely won't be the biggest.

Also, in the rare case you do need to pass it, it would surely be used by the component. That would likely make it many times slower. Currently the test code doesn't do anything with these large values.

let renderTimes = 0;
let compRenderTimes = 0;

function randomString(length) {
    var result           = '';
    var characters       = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for ( var i = 0; i < length; i++ ) {
      result += characters.charAt(Math.floor(Math.random() *  charactersLength));
   }
   return result;
}

const {useState, useEffect, memo} = React;

let lastCompRenderStart;

function Comp (props) {
  compRenderTimes++;
  lastCompRenderStart = performance.now();
  
  // Expensive task.
  //console.log(props);
  /*Object.entries(props).forEach(([k,v]) => {
    if (`${k}${v}` === '4abc') {
      alert('What a coincidence!');
    }
  });*/
  
  useEffect(()=>{
    const duration = performance.now() - lastCompRenderStart;
    document.getElementById('lastCompRenderTime').innerHTML = duration.toFixed(2);
    document.getElementById('nCompRenders').innerHTML = compRenderTimes;
  });

  return <p className="test">Testing {Object.keys(props).length} props, last rendered {performance.now()}</p>;
};

const MemoComp = memo(Comp);

let lastProps = {};
let lastRenderStart;
let lastWasHit = false;
let currentTotal = 0;
let memoRenders = 0;
let memoHitTotal = 0;
let memoHitRenders = 0;
let memoMissTotal = 0;
let memoMissRenders = 0;
let nomemoRenders = 0;
let nomemoTotal = 0;

function App() {
  renderTimes++;
  const [,refresh] = useState();
  const [propAmount, setPropAmount] = useState(10);
  const [propSize, setPropSize] = useState(10);
  const [changeInterval, setChangeInterval] = useState(10);
  const [changedPropOffset, setChangedPropOffset] = useState(0);
  const [withMemo, setWithMemo] = useState(true);
  
  useEffect(()=>{
    
    renderTimes = 1;
    compRenderTimes = 1;
    currentTotal = 0;
    memoRenders = 0;
    memoHitTotal = 0;
    memoHitRenders = 0;
    memoMissTotal = 0;
    memoMissRenders = 0;
    nomemoRenders = 0;
    nomemoTotal = 0;
  }, [propAmount, propSize, changeInterval, changedPropOffset]);
  
  let props = {};
  lastWasHit = renderTimes !== 1 && renderTimes % changeInterval !== 0;
  if (lastWasHit) {
    // Reuse last props;
    props = lastProps;
  } else {
    // Generate random new values after offset.
    for (let i = 0; i < propAmount; i++) {
      if (!!lastProps[i] && (i * 100 / propAmount < changedPropOffset) && i < propAmount - 1) {
        props[i] = lastProps[i];
      } else {
        props[i] = randomString(propSize);
      }
    }
    lastProps = props;
  }

  useEffect(()=>{
    const duration = performance.now() - lastRenderStart;
    document.getElementById('lastRenderTime').innerHTML = duration.toFixed(2);
    
    if (!withMemo) {
      nomemoRenders++;
      nomemoTotal += duration;
      document.getElementById('no-memo-renders').innerHTML = nomemoRenders;
      document.getElementById('average-no-memo').innerHTML = (nomemoTotal / nomemoRenders).toFixed(2);

    } else {
      memoRenders++;
      currentTotal += duration;
      document.getElementById('memo-renders').innerHTML = memoRenders;
      document.getElementById('average').innerHTML = (currentTotal / memoRenders).toFixed(2);

      if (lastWasHit) {
        memoHitRenders++;
        memoHitTotal += duration;
        document.getElementById('average-memo-hit').innerHTML = (memoHitTotal / memoHitRenders).toFixed(2);
      } else {
        memoMissRenders++;
        document.getElementById('memo-renders-miss').innerHTML = memoMissRenders;

        memoMissTotal += duration;
        document.getElementById('average-memo-miss').innerHTML = (memoMissTotal / memoMissRenders).toFixed(2);
      }
    }
  });

  const ActualComp = withMemo ? MemoComp : Comp;
  
  // This should give us the time needed for rendering the rest.
  // I assume the settings inputs have has a more or less constant small impact on performance, at least while they're not being changed.
  lastRenderStart = performance.now();

  return <div>
    <button onClick={() => refresh(renderTimes)} title='Trigger a render of App component'>render</button>
    <input type='checkbox' onChange={event=>setWithMemo(!withMemo)} checked={withMemo}/>
    <label  onClick={event=>setWithMemo(!withMemo)}>
     with memo - 
    </label>
    - Prop amount: <input type='number' title='How many props are passed to memoed component' value={propAmount} onChange={event=>setPropAmount(event.target.value)}/>
    Prop size: <input type='number' title='How many random characters in prop value string?' value={propSize} onChange={event=>setPropSize(event.target.value)}/><br/>
    Change interval: <input type='number' title='Change memoized component props every X renders.' value={changeInterval} onChange={event=>setChangeInterval(event.target.value)}/>
    Changed prop offset <input type='number' min={0} max={100} step={10} title='Leave the first X percent of the props unchanged. To test if props comparison algorithm is affected by how fast it can find a difference. The last prop is always changed.' value={changedPropOffset} onChange={event=>setChangedPropOffset(event.target.value)}/>
    <ActualComp {...props} />
  </div>;
};


ReactDOM.render(<App/>, document.getElementById('root'));
#lastRenderTime {
  background: yellow;
}

#lastCompRenderTime {
  background: lightblue;
}

.test {
  background: lightgrey;
  border-radius: 4px;
}

td {
  border: 1px solid lightgrey;
  padding: 4px;
 }
 
 input[type=number] {
     max-width: 72px;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

<div id="root"></div>

<table>
<th>
  <td>N renders</td>
  <td>Average duration</td>
  <td>Memo (hit) duration</td>
  <td>Memo (miss) duration</td>
</th>
<tr>
<tr>
<td>No memo</td>
<td><span id="no-memo-renders"></span></td>
<td><span id="average-no-memo"></span></td>
</tr>


<tr>
<td>memo</td>
<td><span id="memo-renders"></span>, <span id="memo-renders-miss"></span> miss</td>
<td><span id="average"></span></td>
<td><span id="average-memo-hit"></span></td>
<td><span id="average-memo-miss"></span></td>
</tr>
</table>
=====
<table>
  <tr>
<td>Last rendered App</td>
<td><span id="lastRenderTime"></span></td>
</tr>
<tr>
<td>Component renders</td>
<td><span id="nCompRenders"></span></td>
</tr>
<tr>
<td>Last rendered Component</td>
<td><span id="lastCompRenderTime"></span></td>
</tr>
</table>
inwerpsel
  • 2,677
  • 1
  • 14
  • 21
0

Yes you should, if you want the easy way, You can use it everywhere and it might be the easiest way for optimization.

But you should know how React works, you can write your component in a way that they don't need re-render. but sometime it might be harder than using memo

React will re-render every child, you can prevent this by using memo, but what if you write the parent in a way that doesn't need re-render at all?

There is some design that you can use to achieve this.

1. Move states from parent to children

Imagine we have a page for creating Blog posts. There is a big state in the parent that store everything about the post form. So when every single form element changes, all components will be re-render. Now you can use 'memo' to prevent this, but that is not the way react works.

First, we need to move the state into some store with a selector, you can use something like redux or other store management, I prefer using context with use-sync-external-store/with-selector.

Now the parent should NOT use this store at all, for example, something like this is wrong

[postState, dispatch] = usePostStore();

Because you subscribe to all changes in the store and with anything change all component will re-render again.

To make this work we don't need to use state at the parent (you still can use dispatch to change the state and move logic to reducer so you have access to state there), so something like this should work:

[, dispatch] = usePostStore((state) => null)

so how to manage children? for example for the Editor component in the post editing page, we can subscribe only to state.content

[{content}, dispatch] = usePostStore(({content}=> ({content}))

So when the content changed we only re-render the Editor component, not all component trees.

You can keep doing this with every single form element but it's not that much necessary. you can handle some elements in one component. Of course, when something changes all components will re-render but these are small components so the speed does not matter that much.

For example, can put content and title in the Editor component and it should be ok.

2. Pass component as a child of a parent component

The other thing you can do is pass the component as a child to another component, in this way when the parent component needs to re-render react will not re-render the child.

For example, we can do this:

function Post() {
    return (
      <EditorComponent>
       <FilePicker />
      </EditorComponent>
   );
}

So now when EditorComponent need to re-render React WILL NOT re-render FilePicker, it will only re-render when Post need that and it should happen very few time since the post has minimum state and we tried to put all state in children instead of parent.

Lets see your example with store

// NameComponent.js
import React from "react";
const NameComponent = () => {
   const [name, dispatch]= useStore((store) => store.name);
   return <div>{name}</div>
};
export default NameComponent;

// CountComponent.js
import React from "react";
const CountComponent = () => {
   const [count , dispatch]= useStore((store) => store.count );
   return <div>{count}</div>
};
export default CountComponent;

// App.js
import React from "react";
import NameComponent from "./NameComponent";
import CountComponent from "./CountComponent";

function App() {
  const [, dispatch] = useStore((store) => null);

  handleClick = e => {
    // since we don't have access to `count` here we dispatch it
    // in reducer we have access to count so we can increase it by one
    dispatch('increaseCount');
  };

  return (
    <div>
      <NameComponent name={this.state.name} />
      <CountComponent count={this.state.count} />
      <button onClick={this.handleClick}>Add Count</button>
    </div>
  );
}


const reducer = (state, {type, payload})=> {
   if(type === 'increaseCount'){
      return {
         ...state,
         count: state.count + 1,
      }
   }

   return state;
}

As you can see we don't need state in the parent component, and because of that we don't need memo because the component will only re-render when they subscribed value of the state changes. But what if you need? just extract that to another child component.

Only use state in parent component when lots of component need to change if the value changes

For other options let's look at react document:

In practice, you can make a lot of memoization unnecessary by following a few principles:

  1. When a component visually wraps other components, let it accept JSX as children. This way, when the wrapper component updates its own state, React knows that its children don’t need to re-render.
  2. Prefer local state and don’t lift state up any further than necessary. For example, don’t keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library.
  3. Keep your rendering logic pure. If re-rendering a component causes a problem or produces some noticeable visual artifact, it’s a bug in your component! Fix the bug instead of adding memoization.
  4. Avoid unnecessary Effects that update state. Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over.
  5. Try to remove unnecessary dependencies from your Effects. For example, instead of memoization, it’s often simpler to move some object or a function inside an Effect or outside the component.
Ali Akbar Azizi
  • 3,272
  • 3
  • 25
  • 44
0

tl;dr There is essentially no downside

Optimizing with memo is only valuable when your component re-renders often with the same exact props, and its re-rendering logic is expensive. If there is no perceptible lag when your component re-renders, memo is unnecessary. Keep in mind that memo is completely useless if the props passed to your component are always different, such as if you pass an object or a plain function defined during rendering. This is why you will often need useMemo and useCallback together with memo.

There is no benefit to wrapping a component in memo in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible.

https://react.dev/reference/react/memo

React.memo is ineffective if the props change each render, e.g.

  1. Callers pass event handlers that aren't created with useCallback.
  2. Callers pass other non-primitives (objects, arrays) that aren't created with useMemo.
  3. Callers pass children that aren't created with useMemo.

That said, the cost is close to nothing and it is up to the caller whether to be careful about passing consistent props.

IMO the callee should always use React.memo, and if the caller wants to take advantage of that, it can.


P.S. Whey isn't it used everywhere, as part of React? The primary reason it that is can break rendering with mutable props. But mutable props is a bad practice anyway and most of the community avoids it.

Paul Draper
  • 78,542
  • 46
  • 206
  • 285
-7

"Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.

You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance. (For rare cases when a value must never be recomputed, you can lazily initialize a ref.)"

https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

Taylor Belk
  • 162
  • 1
  • 16
  • 15
    totally unrelated to the question. your answer refers to `useMemo` hook which is **not** to be confused with `react.memo` function – vsync Aug 04 '19 at 14:30