Does a React component deeply compare props to check if rerender is needed?

I would like to know if a React component extending React.Component deeply compares an object when trying to decide if it needs to rerender.

For instance, given

const Foo = ({ bar }) => {
  return <div>{bar.baz}</div>
}

class App extends React.Component {
  constructor() {
    super()
    this.state = { bar: { baz: 1} }
  }

  render() {
    return <Foo bar={this.state.bar} />
  }
}

If inside App, the state bar changes to {baz: 2}, does <Foo /> deeply compare the previous prop bar and the newly received prop?

Incidentally, the docs for PureComponent says

Extend PureComponent … Or, consider using immutable objects to facilitate fast comparisons of nested data.

But does not go into much more details. Any ideas?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Unless you implement a shouldComponentUpdate lifecycle method yourself, a component extending React.Component won’t compare the props before going through a re-render cycle where it compares the previous and current Virtual DOM to decide what to re-render

Also a component extended using React.PureComponent doesn’t deeply compare props with prevProps and state with prevState but performs shallow comparison to decide, if at all a re-render is needed or not.

For a Functional component from v16.6.0 onwards, React has introduced a Higher Order Function, React.memo which can be used to make a functional component behave as a React.PureComponent

According to the docs:

React.memo is a higher order component. It’s similar to
React.PureComponent but for function components instead of classes.

It can be used like

const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

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.

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.

Method 2

‘Extend PureComponent’ was quoted out of context. The documentation says the opposite,

Only extend PureComponent when you expect to have simple props and state

Because

React.PureComponent’s shouldComponentUpdate() only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences.

React components don’t compare props, unless they implement shouldComponentUpdate, like PureComponent. A component can be re-rendered with exactly same prop (=== equal).

If the state is mutated but isn’t forced to update (this is an antipattern), children won’t be re-rendered.

If the state is mutated and forced to update (this is an antipattern), children are re-rendered:

class App extends React.Component {
  componentDidMount() {
     this.state.bar.baz = 2;
     this.setState(state);
  }
  ...    
}

In order to limit updates to deeply changed props, Foo should implement shouldComponentUpdate with deep comparison, e.g. Lodash isEqual:
class Foo extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return !_.isEqual(this.props, nextProps) || this.state !== nextState;
  }
  ...
}

Since deep comparison may be costly, performance tests should be run to determine whether it provides performance improvements.

Immutable state and props are highly welcome in React because they allow to avoid this problem. If an object changes in some way, it should be replaced with another object, i.e. state update should result in new state and state.bar objects:

class App extends React.Component {
  componentDidMount() {
    this.setState(({ bar })=> ({
      bar: { ...bar, baz: 2 }
    });
  }
  ...    
}

In this case Foo needs to shallowly compare bar object it receives as a prop, so it can be PureComponent, or React.memo (as another answer explains).


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x