Click here to Skip to main content
15,074,626 members
Articles / Web Development / React
Article
Posted 6 Mar 2018

Tagged as

Stats

12.4K views
7 bookmarked

Pure Component In React

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
6 Mar 2018CPOL8 min read
In this article, we learn what is pure component, how its works, and what are the benefits of pure component.

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.

JavaScript
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.

Image 1

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?

Image 2

The actual implementation should be that the render method should call when there is any difference backed with previous and new state and props. 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:

JavaScript
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:

Image 3

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.

JavaScript
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:

Image 4

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:

JavaScript
var studentObj=function(name,age)
		{this.name=name;
		this.age=age};
var obj1=new studentObj('pankaj',24);
var obj2=obj1;

//compare both object
obj1==obj2  //output: true

//create another object

var obj3=new studentObj('pankaj',24)
//compare both object
obj2==obj3  //output: false  because in complex type object reference are checked that 
            //are not same in this case

//check the value of both object
 obj3.name==obj2.name  //output: true because in primitive type value are checked that 
                       //are same in this case

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

JavaScript
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

JavaScript
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.

Image 5

If you define the child component as a simple component instead of the "PureComponent", then child component will start to re-render.

Image 6

So never mutate the array type and object type property, instead of this always assign a new value.

Image 7

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.

Image 8

Don't Bind Function in Render Method

Some developers make the mistake of binding a function into a render method like below:

Image 9

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

JavaScript
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

JavaScript
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.

Image 10

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

JavaScript
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;

Image 11

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.

Image 12

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Pankaj Kumar Choudhary
Student
India India
Pankaj Kumar Choudhary loves Microsoft and Database technologies. He has experience on several database technology like SQL Server, MySQL, Oracle, MongoDB, PostgreSQL . He has knowledge of several technology like Asp.Net MVC, Entity Framework , Android, Php, AngularJS, Node.js, Angular2, React, Ionic and Android.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Moykn12-Mar-18 4:20
MemberMoykn12-Mar-18 4:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.