In React, if you work with React lifecycle hooks and states, then you know very well whenever we use the setState
method to change the state of a React component, then the React component always re-renders and shows the new changes on view. If you don't know about the "setState
" method, then I'll tell you that using the "setState
" method, we can change the state of any React component. When the state of any component changes, it will call the "render
" method again and refresh the view again.
I think this is the best and required approach of the React. Because if we want to make the new changes visible, then this "setState
" method will be used. If we make any change into property (props
) of a component, then the component will not render because props
are a readonly
type and we can't change the values of props
in component. I know the re-rendering approach of the React component (when the state changes) is one of the best features of React, but sometimes this feature generates issues for performance for React applications.
Let's look at an example for a better understanding. I have a component and below is the context for this component.
import React from 'react';
import PropTypes from 'prop-types';
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
city: "Alwar",
}
}
componentDidMount(){
setInterval(()=>{
this.setState(()=>{
return { city: "Alwar"}
})
},2000)
}
render() {
console.log('Main Component render');
return (
<div>
<h2>
{this.state.title}
</h2>
<p>User Name: {this.props.name}</p>
<p>User Age: {this.props.age}</p>
</div>
);
}
}
Main.propTypes ={
name:PropTypes.string.isRequired,
age:PropTypes.number.isRequired
}
Main.defaultProps = {
name: 'Pankaj Kumar Choudhary',
age: 24
};
export default Main;
In the above lines of code, I define some initial props and a state. In "componentWillMount
" life cycle hooks, I use the setInterval
method that will repeat the interation after each 1 second and use the "setState
" method of component. I saved the above code, see the below output in the browser.
Because we are using the "setState
" method, the "setState
" method calls the "render
" function of the component and resets the view for the component. Now the question arises, do we really need to re-render the component each time? If you are thinking about any scenario and getting the answer yes, then you may be right. But if you check the above example, then you find that we call the "setState
" method each time but there was not any change backed with new and previous state after that, it calls the render
method of the component. Does it make any sense?
The actual implementation should be that the render
method should call when there is any difference backed with previous and new state
and prop
s. But the issue is that the React component doesn't handle this. So what will be the solution to resolve this issue? There are two methods. First, if we handle this scenario at our end using the "shouldComponentUpdate
" life cycle. The second one is use the "Pure Component." We can read about the "Pure Component" later, but first we learn how we can handle this at our end.
Now I make some changes into the previous code and the new updated code is below:
import React from 'react';
import PropTypes from 'prop-types';
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
city: "Alwar",
}
}
componentDidMount(){
setInterval(()=>{
this.setState(()=>{
return { city: "Alwar"}
})
},1000)
setInterval(()=>{
this.setState(()=>{
return { city: "Jaipur"}
})
},6000)
}
shouldComponentUpdate(nextProps,nextState){
return nextState.city!=this.state.city?true:false;
}
render() {
console.log('Main Component render '+Date.now());
return (
<div>
<h2>
{this.state.title}
</h2>
<p>User Name: {this.props.name}</p>
<p>User Age: {this.props.age}</p>
</div>
);
}
}
Main.propTypes ={
name:PropTypes.string.isRequired,
age:PropTypes.number.isRequired
}
Main.defaultProps = {
name: 'Pankaj Kumar Choudhary',
age: 24
};
export default Main;
Output:
In the above code, I also implement the "shouldComponentUpdate
" lifecycle hooks, this method call is before the "render
" method call. In this method, we check if the component will render or not. If the return value of this component is 'True
', then the component will re-render, otherwise not. In this method, we can implement our logic and tell the component that it will re-render or not. So I checked the "city
" property of the current and nextState
, if there is any difference backed with city
property of both state
then the "shouldComponentUpdate
" method returns true
otherwise false
.
Such that we can prevent the "unnecessary" rendering of the component. We prevent this rendering at our end, but React also has another option that is "Pure Component."
What is Pure Component?
PureComponent
is the same as component that handles the shouldComponentUpdate
method for us as we did in the previous example. Whenever the state
or props
of the component changes, it does a shallow comparison and checks that the component will re-render or not. Pure component did the same task for us that we did in shouldComponentUpdate
manually but does it internally and increase the performance of our application. A simple component doesn't compare the previous and next state instead of rendering the component whenever the "shouldComponentUpdate
" method is called. Let's update our previous example using the Pure Component.
import React from 'react';
import PropTypes from 'prop-types';
class Main extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
city: "Alwar",
}
}
componentDidMount(){
setInterval(()=>{
this.setState(()=>{
return { city: "Alwar"}
})
},1000)
setInterval(()=>{
this.setState(()=>{
return { city: "Jaipur"}
})
},6000)
}
render() {
console.log('Main Component render '+Date.now());
return (
<div>
<h2>
{this.state.title}
</h2>
<p>User Name: {this.props.name}</p>
<p>User Age: {this.props.age}</p>
</div>
);
}
}
Main.propTypes ={
name:PropTypes.string.isRequired,
age:PropTypes.number.isRequired
}
Main.defaultProps = {
name: 'Pankaj Kumar Choudhary',
age: 24
};
export default Main;
Output:
What is Shallow Comparison?
If you noticed that for Pure Component, I used the "Shallow Comparison" term. Actually, Pure Components do the Shallow comparison instead of deep comparison. Shallow comparison means when checking the previous state and props, if value are primitive types, then it only checks or compares their values. But props or state are complex types like an object and array, then it checks their reference type. In deep checking, instead of reference values are compared. Let's take an example:
var studentObj=function(name,age)
{this.name=name;
this.age=age};
var obj1=new studentObj('pankaj',24);
var obj2=obj1;
obj1==obj2
var obj3=new studentObj('pankaj',24)
obj2==obj3
obj3.name==obj2.name
Never Mutate the Props and State in Parent Component
If you have any pure child component, then never mutate the object and array in props
or state
of parent component. The reason is that if you update the object or array into parent component and you are passing this object or array to pure child component, then the child component will not re-render because, as I explained earlier, Pure Component checks the reference of the props
and state
. Because you update (mutate) the object or array, and reference does not change, so the child component can't detect the changes. Let's take an example.
I created another component and named this component as "child.jsx." This child component is pure in nature.
child.jsx
import React from 'react';
import PropTypes from 'prop-types';
class Child extends React.PureComponent {
constructor(props) {
super(props);
}
render() {
console.log('Child Component render '+Date.now());
return (
<div>
<p>Props Values is {this.props.childProps}</p>
</div>
);
}
}
Child.propTypes ={
childProps:PropTypes.array.isRequired,
}
export default Child;
Now we use this component into "Main.jsx" component that will be the parent component.
main.jsx
import React from 'react';
import PropTypes from 'prop-types';
import Child from '../child/child';
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
name:'Pankaj',
city: "Alwar",
data:[Date.now()]
}
}
addData(){
var arrayData=this.state.data;
arrayData.push(Date.now());
this.setState({data:arrayData});
}
render() {
console.log('Main Component render');
return (
<div>
<p>User Name: {this.state.name}</p>
<p>User City: {this.state.city}</p>
<p>User Data: {this.state.data.join(" ,")}</p>
<Child childProps={this.state.data}></Child>
<input type="button" value="UpdateData" onClick={this.addData.bind(this)} />
</div>
);
}
}
export default Main;
In the "main.jsx" component, I defined the state of component and the state of the "main.jsx" component contains three attributes (name
, city
and data
). The type of "data
" attribute is Array
type and we are passing this as attribute as property of the "Child
" component. In this component, we also create a button and on click event of this button, we update the state of component (update data
attribute).
In "child
" component, we are displaying the value of the "data
" property. Now what will happen as we click on the "UpdateData
" button, is it will update the state of "Main
" component and "Main
" component will re-render, but the child
component will never re-render. Because the reference of "data
" property is not changed.
If you define the child component as a simple component instead of the "PureComponent
", then child component will start to re-render.
So never mutate the array type and object type property, instead of this always assign a new value.
Now make some changes into the "Main.jsx" component as highlighted above, after saving the changes, now you will find that on each button click child if also rendering because now each type reference of the "data
" property is also changing.
Don't Bind Function in Render Method
Some developers make the mistake of binding a function into a render
method like below:
The issue with the above pattern is that when we bind the function into the render
method, it always provides a new instance or reference of the function to the child component in each case of render. For example, if you have a parent component (Main.jsx) and you pass a function as prop
to child component and you bind this function into the render
method of parent component. So whenever render
method of parent component (main.jsx) calls, it passes a new instance to the child component. Because the child component gets the new reference of the prop
, it will render also even though your child component is a Pure Component. Let's look at an example to understand this issue.
Main.jsx
import React from 'react';
import PropTypes from 'prop-types';
import Child from '../child/child';
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
name:'Pankaj',
city: "Alwar",
data:[Date.now()]
}
}
addData(){
var arrayData=this.state.data;
arrayData.push(Date.now());
this.setState({data:new Array(arrayData)});
}
alertMethod(data){
console.log(data);
}
render() {
console.log('Main Component render');
return (
<div>
<p>User Name: {this.state.name}</p>
<p>User City: {this.state.city}</p>
<p>User Data: {this.state.data.join(" ,")}</p>
<Child childProps={(e)=>this.alertMethod(e)} name="pankaj"></Child>
<input type="button" value="UpdateData" onClick={this.addData.bind(this)} />
</div>
);
}
}
export default Main;
child.jsx
import React from 'react';
import PropTypes from 'prop-types';
class Child extends React.PureComponent {
constructor(props) {
super(props);
this.transferData=this.transferData.bind(this);
}
transferData(e){
this.props.childProps(e.target.value);
}
render() {
console.log('Child Component render '+Date.now());
return (
<div>
<input type="text" onChange ={this.transferData}/><br/>
<input type="button" value="Child Button" onClick={this.props.childProps} />
</div>
);
}
}
Child.propTypes ={
childProps:PropTypes.func.isRequired,
}
export default Child;
When you click on the "UpdateData
" method, it will update the "main
" component's state. Because the state changes, it will re-render the view and provide a new reference of the "alertMethod
" to the child component and as a result, the child component also re-renders.
To overcome this issue, we have to avoid the binding of function into the render
method. Instead, we have to bind the method in constructor of the component. Let's modify our main component and check the difference.
Main.jsx
import React from 'react';
import PropTypes from 'prop-types';
import Child from '../child/child';
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
name:'Pankaj',
city: "Alwar",
data:[Date.now()]
}
this.alertMethod=this.alertMethod.bind(this);
}
addData(){
var arrayData=this.state.data;
arrayData.push(Date.now());
this.setState({data:new Array(arrayData)});
}
alertMethod(data){
console.log(data);
}
render() {
console.log('Main Component render');
return (
<div>
<p>User Name: {this.state.name}</p>
<p>User City: {this.state.city}</p>
<p>User Data: {this.state.data.join(" ,")}</p>
<Child childProps={this.alertMethod} name="pankaj"></Child>
<input type="button" value="UpdateData" onClick={this.addData.bind(this)} />
</div>
);
}
}
export default Main;
As you can see that instead of binding into the render
method, we bind the function into the component section. So now, if we click on the "UpdateData
" method, it will not render the child component. Because now it will not provide the new instance of "alertMethod
" every time.
Points to Notice about Pure Component
- Use pure component instead of simple component when you want to stop unnecessary re-rendering of the component.
- Never bind the method or derive data into the
render
method because it increases the re-rendering rate of child component. - Pure component can increase the performance of the application, but use the pure component if you really need it because extra and unnecessary use can degrade the application performance also.
- Not only pure component stops the re-rendering itself, it also stops the re-rendering of the child component. Also best use of the pure component is when you don't have any child component of pure component and don't have any kind of dependency on global state of application.
- Pure component will not render if you mutate the array of object that you are passing as
props
to pure component from the parent component.
Conclusion
In this article, we learned what is pure component, how its works, and what are the benefits of pure component. So the final conclusion is that the use of pure component can increase the performance of your application, so try to use it more and more. If you have any doubts or questions about this article, then write in the comments section below. Thanks for reading this article.