×
Community Blog Your Guide to Efficient Re-rendering with React

Your Guide to Efficient Re-rendering with React

A React component translates raw data into rich HTML. You can control the state of the component by changing its props and state.

Design_be_compact_to_be_elegant

First, let’s understand the process of re-rendering in React. A React component translates raw data (consisting of props and state) into rich HTML. You can control the state of the component by changing its props and state. The entire component is re-rendered whenever its props/state change.


Steps for re-rendering in React:


  1. When the props/state of a component change, React builds a new virtual DOM and compares the new and old virtual DOMs using the diff algorithm.
  2. If the new and old virtual DOM trees are not consistent, re-rendering begins.


1

The DOM operation is very time-consuming. To improve the performance of components, try to minimize unnecessary re-rendering of them.


Consider the following example.


The following figure shows a rendered component.


2

As the state of the component changes, the green node is re-rendered.

3

It is logical to think that only three nodes (highlighted in green) need to be updated to complete the component change.

4

However, as per React’s updating rules, whenever the props or state of a component change, the entire component is re-rendered. So, apart from the three green nodes, all other nodes (highlighted in yellow) are also re-rendered.


5

This is a huge waste of performance. For complex pages, this results in a very sub-standard user-experience. Therefore, to improve component performance, it is imperative to minimize unnecessary rendering.

Component Optimization Techniques


Due to the need for minimizing unnecessary rendering, let’s look at some techniques for component optimization.

Pure Component


A pure component in React is a component whose render function is dependent only on its props and state. With the same props and state, the rendered output receives the same results. The following code represents a pure component.

render() {
     return (
          {this.state.rows}   
     );
}

shouldComponentUpdate function


The function, shouldComponentUpdate, runs before rendering. Its return value determines whether the component is re-rendered.



  • Return value = True

    Whenever the component’s props/state change, the virtual DOM is re-constructed and compared using the diff algorithm. The comparison result decides whether the entire component should be re-rendered.


  • Return value = False

    The component is not re-rendered.


The default return value of the function is True, so re-rendering may occur. When re-rendering is not required, the default return value of the shouldComponentUpdate function is set to False.


The position of the shouldComponentUpdate function during component re-rendering is as shown in the following figure.

6

PureRenderMixin

React provides the official PureRenderMixin plugin, which makes the shouldComponentUpdate function return False. The plugin can reduce unnecessary re-rendering and improve performance to a certain extent. The use of the plugin is as follows:

 
import PureRenderMixin from 'react-addons-pure-render-mixin';
class FooComponent extends React.Component {
  constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
  }

  render() {
    return <div className={this.props.className}>foo</div>;
  }
}

From the source code, you can see that the plugin is overwriting the shouldComponentUpdate function.

shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);}

This method makes a brief comparison between the current state and next state of a component. If the component’s state changes, a False result is returned. Otherwise, the True result is returned. This function can be further decomposed for implementation.

shouldComponentUpdate(nextProps, nextState) {
            return !shallowEqual(this.props, nextProps) ||
                                    !shallowEqual(this.state, nextState);
}

It compares the changes of props/state of the component. In the latest version of React, the basic class of React.PureComponent is provided, and the PureRenderMixin plugin is no longer needed.

Status Comparison

Let’s assume every component uses the PureRenderMixin plugin. Consider the following component.

shouldComponentUpdate(nextProps, nextState) {
            return !shallowEqual(this.props, nextProps) ||
                                    !shallowEqual(this.state, nextState);
}

To change the color of the component to blue, the following code is run.

shouldComponentUpdate(nextProps, nextState) {
            return !shallowEqual(this.props, nextProps) ||
                                    !shallowEqual(this.state, nextState);

}

The state comparison between two simple objects is depicted as:

7

If the two objects compared are complex, such as dates, functions, or objects with multiple nested layers, then the comparisons are either very time-consuming or unavailable.

You can overwrite the shouldComponentUpdate function to make it applicable to any item (including deep comparison/comparison of recursive layers). Deep comparison is time-consuming and hence not recommended. You should try to use simple props and state as well as avoid unnecessary attributes (or attributes calculated by other attributes in the state).

Immutable.js

The comparison of complex data is time-consuming or unavailable in React. But Immutable.js can solve this problem. It returns the same reference for immutable objects and returns a new reference for changed objects. The following code is used for comparison of the state.

shouldComponentUpdate() {
       return ref1 !== ref2;
}

Dynamic/Static Separation


Consider a component, such as a table.


<ScrollTable
width={300}

color='blue'

scrollTop={this.props.offsetTop}
/>

This is a scrollable table. The offsetTop represents the distance of visible area from the upper border of a browser. When the mouse scrolls, the value changes, altering the props of the component. Subsequently, the component is re-rendered.
Now, consider the following code.

<OuterScroll>
&lt;InnerTable width={300} color='blue'/&gt;
</OuterScroll>

The props of the InnerTable component is fixed. So, use of the pureRenderMixin plugin ensures the return value of shouldComponentUpdate as False. Irrespective of the state of OuterScroll and parent component, the InnerTable component is not to be re-rendered. In short, the sub-component isolates the state of the parent component.


By separating the changed and unchanged attributes, re-rendering is reduced, and performance improves. At the same time, the components can be easily separated and reused.

Child Components


In nested multi-layer and complex components, there are many sub-nodes. So, component updates take longer, making the component difficult to maintain. The unidirectional flow of information from parent component to child component may lead to loss of control over the components.

  • Children change over time

    Consider the following component, which is re-rendered every second (please note: this is a theoretical example only and not feasible in React).

class Parent extends Component {
  shouldComponentUpdate(nextProps) {
      return this.props.children != nextProps.children;
  }
  render() {
      return &lt;div&gt;{this.props.children}&lt;/div&gt;;
  }
}
setInterval(() => {
   ReactDOM.render(

      &lt;Parent&gt;

          &lt;div&gt;child&lt;/div&gt;

      &lt;/Parent&gt;

  );
}, 1000);

Children components that need to be re-rendered are recognized using the shouldComponentUpdate function by checking if the children and parent components are identical or not. Children is an attribute of props, so it is same all of the time. In theory, the component is not to be re-rendered. But in practice, it is re-rendered every time.


Let’s look at the structure of children in the following code.


8

Children are relatively complex objects and re-constructed during each component update. In other words, children are dynamically constructed, so the update is not equal every time. As a result, shouldComponentUpdate returns True every time, and the component is re-rendered. We can replace children with a variable in order for the same object to be constructed every time.

Independent children


Consider the following component.

 class TwoColumnSplit extends Component {
      shouldComponentUpdate() {
          return false;
      }
      render() {
          return (
       <div>
                <FloatLeft>{this.props.children[0]}</FloatLeft>
                <FloatRight>{this.props.children[1]}</FloatRight>
            </div>
          );
      }
  } <TwoColumnSplit>
      <TargetContainer/>
      <BudgetContainer/>
   </TwoColumnSplit>

The shouldComponentUpdate function returns False, and the component doesn’t change with outside state changes. This is because the components TargetContainer and BudgetContainer did not get any information from their parent element, and the children and the parent component are isolated. In fact, TwoColumnSplit plays the role of isolation. For components that do not need to get data from the outside, you can isolate them from external changes by returning a False value to reduce re-rendering.

Container and Component


You can also isolate external changes by using component containers. A container is a data layer, with the component responsible for rendering corresponding components based on data obtained, without any data interaction. The following shows a container and its components.

class BudgetContainer extends Component {
  constructor(props) {
      super(props);
      this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
  }
  computeState() {
      return BudgetStore.getStore()
  }
  render() {
return <Budget {...this.state}/>
   }
}

Containers should not have props and children, so a container and its parent component are isolated. Therefore, no re-rendering is needed due to external factors.


To move the position of a component, you only have to put the component in the right position. You can move it to any place, as well as use in different applications and tests. You should consider the source of internal data to write different containers in different environments.

Conclusion


We covered the importance of re-rendering efficiently in React. Also, you learned various techniques for component optimization, including shouldComponentUpdate and PureRenderMixin functions, status comparison, Immutable.js, dynamic/static separation, child components, and containers.

0 0 0
Share on

Alibaba Clouder

1,446 posts | 232 followers

You may also like

Comments