React - Learn From Scratch - Part Four

What we’ll learn in this part,
  • Event Handling in React
  • What is ‘key’ property
  • Passing data from Child component to Parent component
  • Part One
  • Part Two
  • Part Tree

Event Handling in React

 
Let’s check how to do event handling in React components.
 
Handling events in React is similar to how we do it with plain JavaScript but with some syntactic differences.
  1. Events names are written in cameCase (e.g. onClick instead of onclick)
  2. In plain JavaScript, we pass a string but in JSX we pass an event handler (function definition or function reference)
Here are some examples of event handling. Note: As we discussed earlier in part 1, we are not binding events with event handlers separately but when we are creating elements.
 
In the following example, we are providing function definition directly:
  1. <script type='text/babel'>  
  2.         function MyApp(){  
  3.             return (  
  4.                 <div class="maincontainer">  
  5.                     <button onClick={function(){alert('hello');}}>Count Me In 1</button>   
  6.                 </div>  
  7.             );  
  8.         }  
  9.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  10. </script>  
In the following example, we’ve defined our function separately and are providing its reference.
  1. <script type='text/babel'>  
  2.         function HelloFn(e){  
  3.             //Here e is React Event Object  
  4.             alert('hello');  
  5.         }  
  6.   
  7.         function MyApp(){  
  8.             return (  
  9.                 <div class="maincontainer">  
  10.                     <button onClick={HelloFn}>Count Me In 2</button>  
  11.                 </div>  
  12.             );  
  13.         }  
  14.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  15. </script>  
In the following example, we want to pass some data to event handler. We can do this by providing a function to onClick and by calling another function inside it.
  1. <script type='text/babel'>  
  2.         function FnWithParam(val){  
  3.             alert('Value is:' + val)  
  4.         }  
  5.   
  6.         function MyApp(){  
  7.             var v = "Hello world";  
  8.   
  9.             return (  
  10.                 <div class="maincontainer">  
  11.                     {/*Let's call FnWithParam but how to pass some data to it?  
  12.                     Use Anonymous or arrow function and call actual function inside it  
  13.                     */}  
  14.                     <button onClick={() => (FnWithParam(v))}>Count Me In 3</button>   
  15.                 </div>  
  16.             );  
  17.         }  
  18.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  19. </script>  
With JavaScript, we can return false from event handler to stop its default action (e.g. Redirection to a URL is default behavior of hyperlink). In the latest version of React, returning false will not work but we’ll have to call preventDefault() in event handler. In the following example, it will not redirect to the YouTube channel but it will show alert when hyperlink is  clicked. Try removing e.preventDefault() and see what it does.
  1. <script type='text/babel'>  
  2.         function AnchClickHandler(e){  
  3.                 //To Stop default action, use e.preventDefault()  
  4.                 e.preventDefault();  
  5.                 alert('link is clicked');  
  6.         }  
  7.   
  8.         function MyApp(){  
  9.             return (  
  10.                 <div class="maincontainer">  
  11.                      <a href='https://www.youtube.com/c/LearnInUrdu139' onClick={AnchClickHandler} > LearnInUrdu139 </a>   
  12.                 </div>  
  13.             );  
  14.         }  
  15.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  16. </script>  
In the following example, we are calling the function inside curly braces so onClick will not have reference of a function but instead whatever it is returning (In this case ‘undefined’). So we’ll see alert on page load and when we click on button, it will not do anything.
  1. <script type='text/babel'>  
  2.         function HelloFn(e){  
  3.                 alert('hello 4');  
  4.         }  
  5.   
  6.         function MyApp(){  
  7.             return (  
  8.                 <div class="maincontainer">  
  9.                     {/* 
  10.                     Following is wrong as we are not providing  
  11.                     a function to onClick but result of its calling. 
  12.                     We'll see an alert on page load 
  13.                   */}  
  14.   
  15.                     <button onClick={HelloFn()}>Count Me In 4</button>  
  16.                 </div>  
  17.             );  
  18.         }  
  19.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  20. </script>  
In the following example, we are handling textbox change event. Event project contains target property which represents the input control in this case.
  1. <script type='text/babel'>  
  2.         function onChangeHandler(e){  
  3.                 console.log(e.target.value);  
  4.         }  
  5.   
  6.         function MyApp(){  
  7.             return (  
  8.                 <div class="maincontainer">  
  9.                     <input type="text" onChange={onChangeHandler} />  
  10.                 </div>  
  11.             );  
  12.         }  
  13.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  14. </script>  
React passes its own SyntheticEvent object to event handler (e.g. e in above examples). This object is a wrapper around actual event object but it provides standard interface across different browsers.
 
A question comes to mind: How are we going to play with DOM? Let's suppose we want to read value from a textbox and write in another textbox programmatically, how are we going to do this with React? Wait, we’ll learn that soon.
 
Now let’s update our Profiles Management Example. We’ve made the following changes:
  • Removed MyName and ProfileLink components and encapsulated that logic in Profile component
  • We’ve added another property (eid) in our data objects.
  • We’ve passed it as property to Profile component
  • We’ve added a button in Profile component. When this button will be clicked, it will call another function and pass ‘eid’ as parameter. For now, click event handler is just showing that id in alert.
  1. <script type='text/babel'>  
  2.                   
  3.         function Profile(props){  
  4.               
  5.             function handleCountMe(id){  
  6.                 alert(id);  
  7.             }  
  8.             return (  
  9.                 <div class="mycontainer" >  
  10.                     <h3>{props.name}</h3>  
  11.                     <a href={props.url}>{props.urlText}</a>;  
  12.                     <button onClick={()=>(handleCountMe(props.eid)) } >Count Me In </button>  
  13.                 </div>  
  14.             );  
  15.         }  
  16.   
  17.         function Profiles(props){  
  18.             //Note we hadn't used {} as function body but used () to show body of arrow function  
  19.             var profilesElem = props.data.map(  
  20.                     (obj)=>(<Profile key={obj.id} eid={obj.id} name={obj.name} url={obj.url} urlText={obj.urlText} />)  
  21.                 );  
  22.             return profilesElem;  
  23.         }  
  24.          
  25.         //Created a top level component  
  26.         function MyApp(){  
  27.               
  28.             //hard coded data for now  
  29.             var data = [  
  30.                 {id: 1, name:"Bilal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials"},  
  31.                 {id: 2, name:"Faisal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 2"},  
  32.                 {id: 3, name:"Waqas Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 3"},  
  33.                 {id: 4, name:"Khurram Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 4"}  
  34.                 ];  
  35.               
  36.             return (  
  37.                 <div class="maincontainer">  
  38.                     <Profiles data={data} />  
  39.                 </div>  
  40.             );  
  41.         }  
  42.   
  43.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  44.     </script>  

What is key property?

 
When we generate same component multiple times (e.g. a list), React demands for a (unique) key attribute added to each child item. If we run the above code without key attribute, we’ll see some error on console, i.e. “Each child in a list should have a unique “key” prop.
 
To fix the above error, we’ve added “key” prop to Profile component and used “eid” as its value as “eid” will be unique for each profile. Key must only be unique among ‘siblings’
React needs to know which components are changed and require re-rendering. React uses this “key” attribute to make this decision. If we don’t use key attribute, it may create performance issues in rendering as it will have to re-render the whole list. Keys don’t pass to the component as ‘props’. If we need to use value of this key in component, we should pass it separately as we have done in above example i.e. eid={obj.id}.
 

Sending Something from Child Component to Parent

 
We’ve learned how to pass some data from parent (host) component to child components. For example from ‘MyApp’ to ‘Profiles’ and then from ‘Profiles’ to ‘Profile’ component. We used props.
 
What if child component (e.g. Profile) wants to send data back to Parent component (e.g. Profiles)? We use callback functions concept. We pass a function reference as props to child. Child component may call this function & pass data to it. As function definition is in Parent, parent can take some action when it will be called. A little confusing?
 
Let’s update our Profiles management example and notify Profiles component when something happens in Profile component (e.g. user has clicked button three times in Profile component).
 
In this code snippet, we’ve made the following changes
  • Created a new function ‘LimitCrossHandler’ inside ‘Profiles’ component. When this will be called, caller will pass id & counter to it. Who is going to call this?
  • We’ve created a new ‘prop’ in ‘Profile’ component. We are passing reference of ‘LimitCrossHandler’ function as props.
  • In ‘Profile’ component, we’ve updated ‘handleCountMe’ function and whenever we click a function, counter value is incremented. Once counter value becomes equal or greater than three, we are calling Parent function (i.e. onLimitCross prop has reference to that function) and passing id & counter to it. In other words, parent component will receive id & counter data sent by child component.
  1. <script type='text/babel'>  
  2.           
  3.         function Profile(props){  
  4.             var counter = 0;  
  5.             function handleCountMe(id){  
  6.                 counter++;  
  7.                 if(counter >= 3){  
  8.                     if(props.onLimitCross){  
  9.                         props.onLimitCross(id, counter);  
  10.                     }  
  11.                 }  
  12.             }  
  13.             return (  
  14.                 <div class="mycontainer" >  
  15.                     <h3>{props.name}</h3>  
  16.                     <a href={props.url}>{props.urlText}</a>;  
  17.                     <button onClick={()=>(handleCountMe(props.eid)) } >Count Me In </button>  
  18.                 </div>  
  19.             );  
  20.         }  
  21.   
  22.         function Profiles(props){  
  23.             function LimitCrossHandler(id, counter){  
  24.                 alert('Current value of Counter for id:' + id + ' is: ' + counter);  
  25.             }  
  26.             //Note we hadn't used {} as function body but used () to show body of arrow function  
  27.             var profilesElem = props.data.map(  
  28.                     (obj)=>(  
  29.                         <Profile   
  30.                         key={obj.id}   
  31.                         eid={obj.id}   
  32.                         name={obj.name}   
  33.                         url={obj.url}   
  34.                         urlText={obj.urlText}   
  35.                         onLimitCross={LimitCrossHandler}  
  36.                         />)  
  37.                 );  
  38.             return profilesElem;  
  39.         }  
  40.          
  41.         //Created a top level component  
  42.         function MyApp(){  
  43.               
  44.             //hard coded data for now  
  45.             var data = [  
  46.                 {id: 1, name:"Bilal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials"},  
  47.                 {id: 2, name:"Faisal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 2"},  
  48.                 {id: 3, name:"Waqas Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 3"},  
  49.                 {id: 4, name:"Khurram Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 4"}  
  50.                 ];  
  51.               
  52.             return (  
  53.                 <div class="maincontainer">  
  54.                     <Profiles data={data} />  
  55.                 </div>  
  56.             );  
  57.         }  
  58.   
  59.         ReactDOM.render(<MyApp />,document.getElementById('app'));  
  60.     </script>  

Exercises

  1. Try to show counter variable in Profile component. Check if its UI is updated when its value is changed. If not, why not? We’ll discuss its answer in the next parts.
  2. Add a new button in Profile Component, ‘Decrease Counter’. When this button is clicked, decrease the counter value. Counter value can’t be –ve. Notify parent component through another callback if user tries to make it –ve.

Summary

 
In this article, we learned event handling in React. It is not different from what  we do with plain JavaScript. We learned about the importance of ‘key’ property. Then we saw how we can pass data from child to parent using same callbacks concept we see a lot with normal JavaScript or in JQuery. In the next part, we’ll learn about how to update our UI once it is already rendered.
 
References
  • https://reactjs.org/docs/handling-events.html
  • https://reactjs.org/docs/events.html
  • https://reactjs.org/docs/lists-and-keys.html#keys