0

I recently read an article about performance optimization in React and i just wanted to make sure i understand one of the key concepts.

Let's say i have this component:

class NewComponent extends React.Component {
    render() {
        return (
            <User>
               name={"test"}
            </User>
        )
    }
}

If NewComponent's state changes, it will not re-render User, right? Since User will be the same instance with the same String prop every time.

However, if i write something like this:

class NewComponent extends React.Component {
    render() {
        return (
            <User>
               onChange={(value1, value2) => {
                // do some stuff
             }}
               name={"test"}
            </User>
        )
    }
}

User will always get re-rendered since React is creating and passing a new instance of the onChange function on every render.

Is that true? When it's just a component, the same instance is being passed, but when there are objects/arrays/functions involved, React creates a new instance on every parent render?

Gambit2007
  • 3,260
  • 13
  • 46
  • 86
  • In both cases `User` will get re-rendered, unless you use something like `React.memo` for the `User` component and it doesn't take any "new" `props` - https://reactjs.org/docs/react-api.html#reactmemo. Also, what is `name={"test"}` supposed to do? – goto Feb 11 '20 at 14:45
  • @goto1, `name={"test"}` is just a prop. – Gambit2007 Feb 11 '20 at 14:46
  • A `prop` to what? Currently it's just looks like you're passing `name={"test"}` as a string to `User`'s `children` prop, is that what you're trying to do? – goto Feb 11 '20 at 14:47
  • Yes. What if i use `React.memo`? Wouldn't the `User` component re-render every time then? See this as an example: https://www.freecodecamp.org/news/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36/ – Gambit2007 Feb 11 '20 at 14:52
  • If the `props` don't change, then no it will not. However, be careful with premature optimizations because it can backfire, unless you really have performance issues, then I'd suggest not worrying about it. In the example below, you're passing `name={"test"}` as a string to the `children` prop of the `User` component - this will **never** change. It'd be different, however, if you did something like ``, for example, where the `name` prop could actually change its value & cause the `User` component to re-render, even if using `React.memo` – goto Feb 11 '20 at 14:57
  • Also, something like `React.memo`, as per docs, "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 it will be able to tell that you're passing the same string with value of `test` and skip the re-render. If you're passing more complex things, like `objects` or `functions`, then it will probably cause a re-render, so you'd need your own custom implementation to compare `props` and decide whether to re-render or not. – goto Feb 11 '20 at 15:02
  • So everything that you mentioned only happens when you use `React.memo` and `React.PureComponent`? The shallow props comparison doesn't happen with "normal" functional/class components like the one i gave as an example? – Gambit2007 Feb 11 '20 at 15:08
  • 1
    That's correct - those components would re-render if the parent component itself re-renders from receiving new `props` and/or updating its `state`. Again, this might not be a bad thing unless you're running into performance issues after you measure, so be careful about premature optimizations because they could actually yield a worse performance than what you'd get by not doing anything special. – goto Feb 11 '20 at 15:13
  • By "performance optimization" in this case i imagine you're talking about the mere fact of using `React.memo` or `PureComponent`? – Gambit2007 Feb 11 '20 at 15:16
  • Yes, although I am not sure if the benefits/drawbacks of using `React.memo` vs. `PureComponent` (from the performance perspective) are exactly the same (not sure about the implementation details), but you could run into worse performance when trying to optimize it. Here's a good read if you're interested - https://kentcdodds.com/blog/usememo-and-usecallback – goto Feb 11 '20 at 15:24
  • Thanks! Also from what i know `React.memo` is for functional components whereas `PureComponent` is for class components. – Gambit2007 Feb 11 '20 at 15:26
  • That's correct, but they're both used for the same purpose. – goto Feb 11 '20 at 15:31
  • 2
    **Update:** from what I've researched, benefits and drawbacks of using `React.memo` with `functional components` vs. using `PureComponent` for `class components` are the same. – goto Feb 11 '20 at 16:02
  • Ok great, thanks for your help! If you want you can post all/some of it as an answer and i'll accept it! – Gambit2007 Feb 11 '20 at 16:06

1 Answers1

1

Without using React.memo for functional components or PureComponents for class components, <User /> will re-render every time <NewComponent /> will receive an updated prop and/or updates its state.

As you can see below, even though props for the <Child /> component don't change, they're still being re-rendered due to the updates in state of the <Parent /> component.

class Parent extends React.Component {
  state = { count: 0 };

  updateState = () => {
    this.setState(prevState => {
      return {
        count: prevState.count + 1
      };
    });
  };

  render() {
    console.log(`Parent.count`, this.state.count);
    return (
      <div>
        <button type="button" onClick={this.updateState}>
          Update state
        </button>
        <Child id="1" />
        <Child id="2" />
      </div>
    );
  }
}

function Child(props) {
  console.log(`Child ${props.id} re-/rendered!`);
  
  return null;
}

ReactDOM.render(
  <Parent />,
  document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

If you want to skip re-renders, you could use React.memo, which according to React's documentation, "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." Meaning, if you're passing simple scalar values, such as string, number, or boolean, it will skip the re-render. However, if you have more complex objects, this might not work.

Note below how the complexProp, for the second button, causes a re-render, even though it's using React.memo.

class Parent extends React.Component {
  state = { count: 0 };

  updateState = () => {
    this.setState(prevState => {
      return {
        count: prevState.count + 1
      };
    });
  };

  render() {
    console.log(`Parent.count`, this.state.count);
    return (
      <div>
        <button type="button" onClick={this.updateState}>
          Update state
        </button>
        <Child id="1" complexProp={() => { console.log(`I am function!`) }} />
        <Child id="2" />
      </div>
    );
  }
}

const Child = React.memo(
  function Child(props) {
    console.log(`Child ${props.id} re-/rendered!`);
    return null
  }
)

ReactDOM.render(
  <Parent />,
  document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Read more about shallow comparison:

As for your performance question, I'd suggest to be careful with premature optimizations because they could cause performance problems too. If you have visible performance issues that can be measured, then I'd suggest reaching for tools like React.memo and/or PureComponent. Otherwise, I would not worry about it, even though your components re-render - it's not always a bad thing.

Here's a good read how premature optimizations could cause issues:

Performance optimizations are not free. They ALWAYS come with a cost but do NOT always come with a benefit to offset that cost.

Therefore, optimize responsibly.

Community
  • 1
  • 1
goto
  • 4,336
  • 15
  • 20