PureComponent introduction

React PureRenderMixin is a rewrite of the React shouldComponentUpdate method to optimize React performance. But since React15.3 added a new PureComponent class that makes it easy to use in your own components, you can simply replace Component with PureComponent, so the authors recommend using today’s PureComponent.

PureComponent principle

We know that a change in the component’s state and props will trigger render, but if the component’s state and props are not changed, the render will not execute, and only if PureComponent detects a change in the state or props, PureComponent calls the Render method, so you can improve performance without having to write extra checks manually. React actually makes the outermost shallow comparison:

if(this._compositeType === CompositeTypes.PureClass) { shouldUpdate = ! shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState); }Copy the code

ShadowEqual checks only the props and state of the component, so nested objects and arrays are not compared. A deep comparison is a time consuming operation that iterates through the key values of the enumeration level by level. If comparing deep cases, you can also use shouldComponentUpdate to manually compare individual cases to see if they need to be rerendered. The easiest way to do this is to compare props or state directly:

shouldComponentUpdate(nextProps, nextState) {
  return nextProps.xxx.xx === props.xxx.xx;
}
Copy the code

In addition, you can use the immutable property, immutable. Js is an immutable library. In this case, attribute comparison is very easy because existing objects are not changed and new objects are created instead.

Using PureComponent

Pit crawl 1: Volatile data cannot be used with a reference

PureComponent saves us time and unnecessary code. Well, it’s very important to know how to use it properly, otherwise it won’t work if it’s not used properly. For example, let’s consider a case where the parent component has a Render method and a click handler:

handleClick() {
  let {items} = this.state

  items.push('new-item')
  this.setState({ items })
}

render() {
  return (
    <div>
      <button onClick={this.handleClick} />
      <ItemList items={this.state.items} />
    </div>
  )
}
Copy the code

Since ItemList is a pure component, clicking on it won’t render, but we did add a new value to this.state.items, but it still points to a reference to the same object. However, this can easily be changed by removing mutable objects so that they are rendered correctly.

handleClick() {
  this.setState(prevState => ({
    words: prevState.items.concat(['new-item'])}}))Copy the code

Pit crawl 2: Invariant data using a reference

The above mutable data cannot be used in a reference case where there is a click delete operation, if we delete the code like this:

constructor(props){
    super(props)
    this.state={
        items: [{a: 1}, {a: 2}, {a: 3}]
    }
}
handleClick = () => {
  const { items } = this.state;
  items.splice(items.length - 1, 1);
  this.setState({ items });
}
Copy the code

Items references are also changed, but state.items[0] === nextState.items[0] is false if items contain reference type data, and the child components are still re-rendered. This requires us to ensure that references to immutable child component data cannot be changed. In this case, we can use the imMUTABLE js library.

Pit crawl 3: The parent passes the callback function to the child via props

When we pass the callback function in parent-child communication:

// step1
<MyInput onChange={e => this.props.update(e.target.value)} />
// step2
update(e) {
  this.props.update(e.target.value)
}
render() {
  return <MyInput onChange={this.update.bind(this)} />
}

Copy the code

Since each render operation on MyInput’s onChange property returns a new function, the parent component’s render also causes MyInput’s render, even if it hasn’t changed anything, so avoid this. Best package:

update = (e) => {
  this.props.update(e.target.value)
}
render() {
  return <MyInput onChange={this.update} />
}
Copy the code

Pit crawl 4: New arrays, empty arrays, will also cause components to be re-rendered

<Entity values={this.props.values || []}/>
Copy the code

To avoid this problem, you can use defaultProps, which contains an initialized null state for the property. When a PureComponent is created, it gets new data and rerenders because the function’s new object is created. The easiest way to solve this problem is to use bind in the component’s constructor method.

constructor(props) {
    super(props)
    this.update = this.update.bind(this)
}
update(e) {
    this.props.update(e.target.value)
}
render() {
    return <MyInput onChange={this.update} />
}
Copy the code

Also, in JSX, shallowEqual checks always return false for any component that contains child elements.

PureComponent usage

The usage is simple:

import React { PureComponent, Component } from 'react';

class Foo extends (PureComponent || Component) {
  //...
}
Copy the code

React is compatible with older versions of React.

conclusion

1. Ignoring rerendering of pure components affects not only the component itself but also its child elements, so the best case for using PureComponent is to present components that have neither child components nor depend on the global state of the application

ShallowEqual is not going to pass, but you can’t use the same reference for props and state.