React - Learn From Scratch - Part Six

In this wesires we’ll learn,
  • What is ‘this’ keyword in JavaScript
  • What is ‘bind’ method
  • How ‘this’ works with Arrow functions
  • Using above concepts in React example
  • Please review Part 1, Part 2, Part 3, Part 4, Part 5
Let’s learn more about ‘this’ keyword as its correct understanding will save us from lot of pain later.
 

Understanding of ‘this’ keyword in JavaScript

 
“this” inside a function represents “caller”. If a function is part of an object, we can call that function using dot (.) notation (e.g. myObj.Show()). In this case, “this” inside the “Show” function represents (or is an alias of) “myObj” object. We can use a different caller for a function by using ‘call’ or ‘apply’ methods. Flexibility to have a different caller of the same function is a powerful feature but sometimes we don’t want this.
  1. <script >  
  2.         class Person{  
  3.             a = 10;  
  4.             constructor(){  
  5.             }  
  6.             show(){  
  7.                 console.log('value of a is:' + this.a);  
  8.             }  
  9.         }  
  10.   
  11.         var obj = new Person();  
  12.         //'this' refers to 'obj' and 'obj' has a = 10  
  13.         obj.show(); //output will be: '10'  
  14.   
  15.         //func now points to same show function  
  16.         var func = obj.show;  
  17.         func(); //Error as 'this' is undefined  
  18.   
  19.     </script>  
Sometimes we want to use a “predefined” object for “this” irrespective of caller. There are multiple ways of achieving this.
 

Binding an object as ‘this’ with a function using ‘bind’ method

 
‘bind’ method takes an object and creates a copy of the function but creates a binding (provided object will be considered as ‘this’ inside function). In the following example, we created a copy of ‘show’ function using ‘bind’ method. ‘obj’ is passed to the bind method so ‘this’ inside ‘show’ function will be ‘obj’ object. It doesn’t matter who is the caller.
  1. <script >  
  2.         var obj = {  
  3.             a: 10,  
  4.             show: function (){  
  5.                 console.log(this);// Check what it is showing  
  6.                 console.log(this.a);    
  7.              }  
  8.         };  
  9.   
  10.         obj.show(); //output will be: 10  
  11.           
  12.         var func = obj.show;  
  13.         //func is being called without a caller means at global scope (i.e. Window object in browser)  
  14.         func(); //output will be: undefined,   
  15.           
  16.         console.log('---------------------');  
  17.           
  18.         //Let's update show with 'bind'  
  19.         //bind will create a new function and 'this' will be whatever we are passing to it.  
  20.         obj.show = obj.show.bind(obj)  
  21.         obj.show(); //output will be: 10  
  22.   
  23.         var func2 = obj.show;  
  24.   
  25.         func2(); //output will be: 10  
  26.   
  27.     </script>  

Binding an object as ‘this’ with a function using Arrow function syntax

 
Arrow functions also give us this flexibility. In arrow function, “this” keyword always refers to the object which has “defined” (or owner of) the arrow function.
 
Let’s check an example without arrow function. Run the script and check output on console.
  1. <script >  
  2.        class Person{  
  3.            a = 10;  
  4.            show() {  
  5.                console.log(this.a);  
  6.            }  
  7.   
  8.        }  
  9.        var obj = new Person();  
  10.        obj.show();//output will be:10  
  11.   
  12.        var func = obj.show;  
  13.        func(); //Error: In this case 'this' will be undefined  
  14.   
  15.    </script>  
Now let’s update the above example and use arrow function syntax to create ‘show’ function.
  1. <script >  
  2.         class Person{  
  3.             a = 10;  
  4.             show = (msg) => {  
  5.                 console.log(msg + ' ' + this.a);  
  6.             }  
  7.   
  8.         }  
  9.         var obj = new Person();  
  10.         obj.show('with object:');//output will be:10  
  11.   
  12.         var func = obj.show;  
  13.         func('without any object:'); //output will be:10  
  14.           
  15.     </script>  
In the above example, who is the creator/owner of show function? When we created a new object ‘obj’, obj will be the owner of show function inside object. It means ‘this’ inside show function will always be an alias of ‘obj’.
 
Let’s use both ‘bind’ & arrow function approach in one example.
  1. <script >  
  2.         class Person{  
  3.             a = 10;  
  4.             constructor(){  
  5.                 //Let's bind this using 'bind' method for show2  
  6.                 this.show2 = this.show2.bind(this);  
  7.             }  
  8.             show = (msg) => {  
  9.                 console.log(msg + ' ' + this.a);  
  10.             }  
  11.   
  12.             show2() {  
  13.                 console.log(this.a);  
  14.             }  
  15.   
  16.         }  
  17.         console.log('---------calling with object----------')  
  18.         var obj = new Person();  
  19.         obj.show('with object:');//output will be:10  
  20.         obj.show2();  
  21.   
  22.         console.log('---------calling without object----------')  
  23.         var func = obj.show;  
  24.         func('without any object:'); //output will be:10  
  25.   
  26.         var func2 = obj.show2;  
  27.         func2();  
  28.           
  29.     </script>  
In the  last part of this series, we ended withthe  following example in which we added three buttons. The first two were throwing error and the last one was working as expected. The details of each case are given below.
  1. class Profile extends React.Component{  
  2.            constructor(props1){  
  3.                super(props1);  
  4.            }  
  5.            CountClickHandler(e){  
  6.                //What is 'this' here?  
  7.                alert(this.props.eid);  
  8.            }  
  9.            render(){  
  10.                return (  
  11.                <div class="mycontainer" >  
  12.                    <h3>{this.props.name}</h3>  
  13.                    <a href={this.props.url}>{this.props.urlText}</a>  
  14.                    <button onClick={  
  15.                        function()  
  16.                            {  
  17.                                alert(this.props.eid);  
  18.                            }  
  19.                        } >Count Me In 1</button>  
  20.                    <button onClick={this.CountClickHandler} >Count Me In 2</button>  
  21.                    <button onClick={  
  22.                            ()=> {  
  23.                                var myObj = this;  
  24.                                myObj.CountClickHandler()  
  25.                            }  
  26.                        } >Count Me In 3</button>  
  27.                </div>  
  28.                );  
  29.            }  
  30.        }  
In the following case, we are providing a function to React. This function (event handler) will be called by React. We want ‘this’ inside function to be the object of current component but as function is going to be called by someone else without any object
  1. <button onClick={  
  2.    function()  
  3.    {  
  4.       alert(this.props.eid);  
  5.    }  
  6. } >Count Me In 1</button>  
In the following case, we provided a reference of a function which is defined in the class. Again this function will be called by React internally (not with object of current component).
  1. <button onClick={this.CountClickHandler} >Count Me In 2</button>  
Now in the following case, we’ve provided an arrow function to React as click event handler. We know when an arrow function is created, it keeps track of its creator/owner and uses that as ‘this’ inside it. Whenever this arrow function will be called, ‘this’ will refer to the current component object. We’ve stored it in a variable ‘myObj’ intentionally. Then we are calling a function “myObj.CountClickHandler()”. So what will be ‘this’ inside ‘CountClickHandler()’? That will be the caller. Who is the caller? It is ‘myObj’. And what is ‘myObj’?It was current component object. In short, ‘this’ is behaving as we expect from it.
  1. <button onClick={  
  2.    ()=> {  
  3.       var myObj = this;  
  4.       myObj.CountClickHandler()  
  5.       }  
  6.  } >Count Me In 3</button>  
There is another way of achieving the same thing; i.e., making sure ‘this’ refers to a specific object. Do you remember? Yes, it is by using ‘bind’ method.
 
Let’s update constructor with bind statement. We are creating copy of CountClickHandler() function and providing it ‘this’ (current object) so that it will always use the provided object as ‘this’ internally. Run the page and check if second button is still throwing error or working as expected; i.e. showing eid of profile.
  1. constructor(props1){  
  2.    super(props1);  
  3.    this.CountClickHandler = this.CountClickHandler.bind(this);  
  4. }  
Following is final work of Part 4. Can you convert it to class components without affecting any existing functionality?
  1. <html>    
  2. <head>    
  3.     <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>    
  4.     <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>    
  5.     <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>    
  6.     
  7.     <style>    
  8.         .mycontainer {    
  9.           border:1px solid red;      
  10.           width: 200px;    
  11.           float:left;    
  12.           margin-left:3px;    
  13.           padding: 5px;    
  14.         }    
  15.     </style>    
  16. </head>    
  17. <body>    
  18.     <div id="app">    
  19.     </div>    
  20.     
  21.     <script type='text/babel'>    
  22.             
  23.         function Profile(props){    
  24.             var counter = 0;    
  25.             function handleCountMe(id){    
  26.                 counter++;    
  27.                 if(counter >= 3){    
  28.                     if(props.onLimitCross){    
  29.                         props.onLimitCross(id, counter);    
  30.                     }    
  31.                 }    
  32.             }    
  33.             return (    
  34.                 <div class="mycontainer" >    
  35.                     <span>{counter}</span>    
  36.                     <h3>{props.name}</h3>    
  37.                     <a href={props.url}>{props.urlText}</a>;    
  38.                     <button onClick={()=>(handleCountMe(props.eid)) } >Count Me In </button>    
  39.                 </div>    
  40.             );    
  41.         }    
  42.     
  43.         function Profiles(props){    
  44.             function LimitCrossHandler(id, counter){    
  45.                 alert('Current value of Counter for id:' + id + ' is: ' + counter);    
  46.             }    
  47.             //Note we hadn't used {} as function body but used () to show body of arrow function    
  48.             var profilesElem = props.data.map(    
  49.                     (obj)=>(    
  50.                         <Profile     
  51.                         key={obj.id}     
  52.                         eid={obj.id}     
  53.                         name={obj.name}     
  54.                         url={obj.url}     
  55.                         urlText={obj.urlText}     
  56.                         onLimitCross={LimitCrossHandler}    
  57.                         />)    
  58.                 );    
  59.             return profilesElem;    
  60.         }    
  61.            
  62.         //Created a top level component    
  63.         function MyApp(){    
  64.                 
  65.             //hard coded data for now    
  66.             var data = [    
  67.                 {id: 1, name:"Bilal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials"},    
  68.                 {id: 2, name:"Faisal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 2"},    
  69.                 {id: 3, name:"Waqas Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 3"},    
  70.                 {id: 4, name:"Khurram Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 4"}    
  71.                 ];    
  72.                 
  73.             return (    
  74.                 <div class="maincontainer">    
  75.                     <Profiles data={data} />    
  76.                 </div>    
  77.             );    
  78.         }    
  79.     
  80.         ReactDOM.render(<MyApp />,document.getElementById('app'));    
  81.     </script>    
  82. </body>    
  83.      
  84. </html>    
Here is the updated version. Mainly mistakes will be related to ‘this’ being missed. Keep checking console if page is not working as expected. 
  1. <html>  
  2. <head>  
  3.     <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>  
  4.     <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>  
  5.     <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>  
  6.   
  7.     <style>  
  8.         .mycontainer {  
  9.           border:1px solid red;    
  10.           width: 200px;  
  11.           float:left;  
  12.           margin-left:3px;  
  13.           padding: 5px;  
  14.         }  
  15.     </style>  
  16. </head>  
  17. <body>  
  18.     <div id="app">  
  19.     </div>  
  20.   
  21.     <script type='text/babel'>  
  22.         class Profile extends React.Component{  
  23.             counter = 0;  
  24.             constructor(props){  
  25.                 super(props);  
  26.                 this.handleCountMe = this.handleCountMe.bind(this);  
  27.             }  
  28.             handleCountMe(id){  
  29.                 this.counter++;  
  30.                 if(this.counter >= 3){  
  31.                     if(this.props.onLimitCross){  
  32.                         this.props.onLimitCross(id, this.counter);  
  33.                     }  
  34.                 }  
  35.             }  
  36.   
  37.             render(){  
  38.                 return (  
  39.                 <div class="mycontainer" >  
  40.                     <span>{this.counter}</span>  
  41.                     <h3>{this.props.name}</h3>  
  42.                     <a href={this.props.url}>{this.props.urlText}</a>;  
  43.                     <button onClick={()=>(this.handleCountMe(this.props.eid)) } >Count Me In </button>  
  44.                 </div>  
  45.             );  
  46.             }  
  47.         }  
  48.           
  49.         class Profiles extends React.Component{  
  50.             LimitCrossHandler(id, counter){  
  51.                 alert('Current value of Counter for id:' + id + ' is: ' + counter);  
  52.             }  
  53.   
  54.             render(){  
  55.                 var profilesElem = this.props.data.map(  
  56.                     (obj)=>(  
  57.                         <Profile   
  58.                         key={obj.id}   
  59.                         eid={obj.id}   
  60.                         name={obj.name}   
  61.                         url={obj.url}   
  62.                         urlText={obj.urlText}   
  63.                         onLimitCross={this.LimitCrossHandler}  
  64.                         />)  
  65.                 );  
  66.                 return profilesElem;  
  67.             }  
  68.         }  
  69.          
  70.         //Created a top level component  
  71.         class MyApp extends React.Component{  
  72.             mydata = [  
  73.                 {id: 1, name:"Bilal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials"},  
  74.                 {id: 2, name:"Faisal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 2"},  
  75.                 {id: 3, name:"Waqas Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 3"},  
  76.                 {id: 4, name:"Khurram Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 4"}  
  77.                 ];  
  78.   
  79.             render(){  
  80.                 return (  
  81.                 <div class="maincontainer">  
  82.                     <Profiles data={this.mydata} />  
  83.                 </div>  
  84.             );  
  85.             }  
  86.         }  
  87.           
  88.   
  89.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  90.     </script>  
  91. </body>  
  92.    
  93. </html>  
Question: Do we need to pass ‘this.props.eid’ to handleCountMe() function?
 
Answer
 
No, we don’t need to. Function has access to this.props so it can also get eid from that.
 
Question: If we add another element in our MyApp ‘data’ array programmatically? How to generate relevant profile on UI?
 
Answer
 
React should do that automatically. Was that not one of the main benefit of using React?
 
Let’s update MyApp component. Add a new button and when this button is clicked, add a new object in our ‘mydata’ array. Run page, open console and click button. We can see on console that when we click on button, it is adding a new object in ‘mydata’ row but UI is not being updated.
  1. class MyApp extends React.Component{  
  2.             mydata = [  
  3.                 {id: 1, name:"Bilal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials"},  
  4.                 {id: 2, name:"Faisal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 2"},  
  5.                 {id: 3, name:"Waqas Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 3"},  
  6.                 {id: 4, name:"Khurram Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 4"}  
  7.                 ];  
  8.   
  9.             render(){  
  10.                 return (  
  11.                   
  12.                 <div class="maincontainer">  
  13.                     <div>  
  14.                         <button onClick={  
  15.                             () => {  
  16.                                 this.mydata.push({id:5,name:"Bisaam",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"New Profile"})  
  17.                                 console.log(this.mydata);  
  18.                             }  
  19.                         }>Add Profile </button>  
  20.                     </div>  
  21.                     <Profiles data={this.mydata} />  
  22.                 </div>  
  23.             );  
  24.             }  
  25.         }  
Why is it not updating UI when we are updating our data? That’s what we’ll learn in next article. i.e. What is State in React.
 

Summary

 
In this article we mostly explored JavaScript concepts and then applied them in a React example. We can bind or attach an object with a function so that the function will always use that object as 'this' inside it.
 
For more reading,
 
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind