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.
The DOM operation is very time-consuming. To improve the performance of components, try to minimize unnecessary re-rendering of them.
The following figure shows a rendered component.
As the state of the component changes, the green node is re-rendered.
It is logical to think that only three nodes (highlighted in green) need to be updated to complete the component change.
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.
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.
Due to the need for minimizing unnecessary rendering, let’s look at some techniques for component optimization.
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}
);
}
The function, shouldComponentUpdate, runs before rendering. Its return value determines whether the component is re-rendered.
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.
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.
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.
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:
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;
}
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>
<InnerTable width={300} color='blue'/>
</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
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 <div>{this.props.children}</div>;
}
}
setInterval(() => {
ReactDOM.render(
<Parent>
<div>child</div>
</Parent>
);
}, 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.
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.
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.
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.
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.
2,599 posts | 758 followers
FollowAlibaba Clouder - October 30, 2018
ApsaraDB - May 15, 2024
Alibaba Clouder - March 29, 2019
Yee - August 16, 2021
ApsaraDB - November 17, 2022
Alibaba Clouder - October 26, 2018
2,599 posts | 758 followers
FollowAn array of powerful multimedia services providing massive cloud storage and efficient content delivery for a smooth and rich user experience.
Learn MoreMore Posts by Alibaba Clouder
5449475882196178 December 13, 2019 at 2:20 am
Visit https://www.3drenderingltd.com/ for great services about 3D rendering companies