Gambit2007
Gambit2007

Reputation: 4004

React - performance when returning a component from a parent component

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?

Upvotes: 0

Views: 254

Answers (1)

goto
goto

Reputation: 4445

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.

Upvotes: 1

Related Questions