Introduction
Up until now, you should be clear about the following React concepts. You should go back on the previous parts if you feel weak in any of these concepts.
- What is React and how it is different from jQuery or Plain JavaScript
- What is a React Element
- What is JSX
- What is the Function Component
- Passing Data from Parent/Host Component to Child Component
- Passing Data from Child Component to Host Component
- Event Handling in React
- What is a Class Component
In the last part, we ended with an issue/requirement (i.e. Updating UI when data is changed.)
In this part, we'll learn:
React updates the UI automatically when we change our data, but it doesn’t do this for all data variables. It should not be doing this for all variables, right? It can have watchers (like Angular) to see which variable is being changed and which UI should be updated. The second option is to let React know that we are updating data. React follows the second option. It introduces a special property with the name ‘state’ in the class component only. If we want React to update our UI automatically when the data changes, we need to store such data in ‘state’ property. Is it not easy? Yes, it is, but with some important points. Let’s check these points quickly and then we’ll learn with some examples.
- React.Component class gives us a ‘state’ property in our class component.
- We should initialize ‘state’ property in constructor. Ideally it should be an object which may contain other properties.
- ‘state’ property must never be updated directly e.g. this.state.data = something. Instead we should always use setState() method provided by React.
setState() function
setState() takes an object containing ‘changes’ directly or indirectly. We may pass ‘changes’ object directly to setState() function or we may pass a function to it and then that function returns ‘changes’ object.
- setState(object): We can pass a ‘changes’ object to update the ‘state’ object.
- setState(updaterFn,callbackFn): updaterFn function is called by React internally to update state. When React calls it, React provides current state & props to it. We return the ‘changes’ object from this function. callbackFn function is called by React when the state is updated.
setState() does shallow merging. We provide ‘changes’ object to it and it merges changes in ‘state’ object. Shallow merging means
- It adds properties in ‘state’ object if ‘changes’ object contains but ‘state’ doesn’t
- Matched properties are overwritten in the ‘state’ object
setState() doesn’t update ‘state’ immediately. It also doesn’t update UI immediately. When we call setState(), We are requesting React that this component & its children need to be re-rendered with the updated state.
In the following example, we’ve created a simple Counter component. When we click on the button, it increases the counter variable and shows it in the alert. We also want to show counter value on UI. But the following approach is not updating the UI.
- <html>
- <head>
- <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
- <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
- <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
- </head>
- <body>
- <div id="app">
- </div>
- <script type='text/babel'>
- class Counter extends React.Component{
- counter = 0;
- handleCountMe(){
- this.counter++;
- alert(this.counter);
- }
- render(){
- return (
- <div class="mycontainer" >
- <div>{this.counter}</div>
- <button onClick={()=>(this.handleCountMe()) } >Count Me In </button>
- </div>
- );
- }
- }
- ReactDOM.render(<Counter />,document.getElementById('app'));
- </script>
- </body>
- </html>
Let’s update the above example and use the state management feature of React.
- <html>
- <head>
- <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
- <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
- <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
- </head>
- <body>
- <div id="app">
- </div>
- <script type='text/babel'>
- class Counter extends React.Component{
- constructor(props){
- super(props);
- this.state = {counter:0,dummy:0};
- }
- handleCountMe(){
- //takes callback function
- this.setState((currState)=>{
- //Return object with 'changes'.
- //This 'changes' object will be merged with 'state' object
- return {counter:currState.counter+1};
- });
- }
- render(){
- return (
- <div class="mycontainer" >
- <div>{this.state.counter}</div>
- <button onClick={()=>(this.handleCountMe()) } >Count Me In </button>
- </div>
- );
- }
- }
- ReactDOM.render(<Counter />,document.getElementById('app'));
- </script>
- </body>
- </html>
In the above example,
- We added a constructor and initialized ‘state’ property with an object. We added a 'counter' property with 0 in it. We’ve added another ‘dummy’ property to it. It has no usage in this example but it is there to show one case while updating the state.
- In handleCountMe() function, we have added ‘state’ update logic. It has become a little complex, we know but that is how we are going to set state. We’ve called setState method and provided it a callback function as a parameter. When React will execute this callback function internally, it will pass the current state to it. We need to return an object (with changes only). We are returning an object with a new value of the counter. Note this object doesn’t contain ‘dummy’ property as there is no change in that property. React will merge this object in ‘state’ property.
- Then we’ve used this.state.counter in UI.
Let’s check some more cases to update the state for learning purposes. Let’s uncomment each case snippet to test them one by one. Details of each case are given below.
- <html>
- <head>
- <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
- <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
- <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
- </head>
- <body>
- <div id="app">
- </div>
- <script type='text/babel'>
- class Counter extends React.Component{
- constructor(props){
- super(props);
- this.state = {counter:0,dummy:0};
- }
- handleCountMe(){
- //Case 1 - Will not update UI
- //this.state.counter++;
- //Case 2 - setState with Object
- //this.setState({counter: this.state.counter + 1});
- //Case 3 - setState with Object multiple times (batched)
- // this.setState({counter: this.state.counter + 1});
- // this.setState({counter: this.state.counter + 1});
- // this.setState({counter: this.state.counter + 1});
- // this.setState({counter: this.state.counter + 1});
- //Case 4 - setState with updaterFn
- // this.setState(function(currState, props){
- // return {counter: currState.counter + 1};
- // });
- //Case 5 - setState with updaterFn multiple times
- // this.setState(function(currState, props){
- // return {counter: currState.counter + 1};
- // });
- // this.setState(function(currState, props){
- // return {counter: currState.counter + 2};
- // });
- //Case 6: setState with updaterFn & callbackFn
- // this.setState(function(currState, props){
- // return {counter: currState.counter + 1};
- // },function(){
- // alert('in callback fn' + this.state.counter);
- // });
- // alert('outside ' + this.state.counter);
- //Case 7: Updating state directly and using setState to request React for UI update
- // this.state.counter++;
- // this.setState({});
- //Case 8: Update state inside Update & return empty object
- // this.setState(function(currState, props){
- // currState.counter++;
- // return {};
- // });
- }
- render(){
- return (
- <div class="mycontainer" >
- <div>{this.state.counter}</div>
- <button onClick={()=>(this.handleCountMe()) } >Count Me In </button>
- </div>
- );
- }
- }
- ReactDOM.render(<Counter />,document.getElementById('app'));
- </script>
- </body>
- </html>
Case 1
We are updating the state directly. It will change ‘state’ but UI will not be updated. We must not update ‘state’ directly as it may create unknown issues.
Case 2
We are using setState(object) method with ‘changes’ object as an argument.
Case 3
We are using setState(object) method with the ‘changes’ object as argument. We are calling this method multiple times. In this way, we are creating a batch. As React doesn’t update state immediately, we’ll notice that in fourth ‘setState()’, we’ll still have this.state.counter as 0 and we’ll see 1 on UI not 4. This has happened because following objects are passed considering state is not updated immediately.
- this.setState({counter: 0 + 1});
- this.setState({counter: 0 + 1});
- this.setState({counter: 0 + 1});
- this.setState({counter: 0 + 1});
Case 4
We are passing a function to setState() method. React calls this function internally but not immediately. When this function is called, React passes current state (at that time) to it. React also passes ‘props’ to it. What we return from this function is merged in ‘state’. We don’t need to return whole ‘state’ object but an object with properties with updated values.
Case 5
We are performing Case 4 again but multiple times. Both setState() functions will be called but state will not be updated immediately. Now React has two updaterFn functions in its queue. It will call first updaterFn by passing the current ‘state’ object to it. Currently state.counter is 0. We are adding 1 to it and returning the changes. ‘state’ will be updated with state.counter =1. Now React will call second updaterFn from queue by passing current ‘state' object to it. At this moment, state.counter is 1. We are adding 2 to it in second updaterFn. ‘state’ will be updated with state.counter=3. After all updaterFn functions are executed means state is updated, React may now re-render component & its children.
Case 6
We are passing two functions to setState() method. First is updaterFn as we discussed above. Second function is callback function and called by React when state is updated. So, if we want to do anything once the state is updated, we may do that in this callback function.
Note
As setState() doesn’t update the ‘state’ immediately, we should not expect an updated ‘state’ after function call. We’ll get 0 in alert.
Case 7
In this example, we are calling setState() after updating state directly. We are passing an empty object so it will not make any change to ‘state’ object but will notify React that UI needs to be re-rendered.
Note
We must not update the state directly. This may create issues.
Case 8
In this example, we are passing updaterFn to setState() but inside function we are updating state object directly and then returning object. This will work but we should avoid doing this.
Let’s check another example. In this example, we have only one component i.e. Profiles. We want to show these profiles on UI with a Remove button.
- <html>
- <head>
- <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
- <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
- <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
- <style>
- .mycontainer {
- border:1px solid red;
- width: 200px;
- float:left;
- margin-left:3px;
- padding: 5px;
- }
- </style>
- </head>
- <body>
- <div id="app">
- </div>
- <script type='text/babel'>
- class Profiles extends React.Component{
- mydata =[
- {id: 1, name:"Bilal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials"},
- {id: 2, name:"Faisal Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 2"},
- {id: 3, name:"Waqas Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 3"},
- {id: 4, name:"Khurram Shahzad",url:"https://www.youtube.com/c/LearnInUrdu139",urlText:"Learn in Urdu Tutorials 4"}
- ];
- constructor(props){
- super(props);
- this.state = {data:this.mydata};
- }
- RemoveProfileHandler(id){
- alert(id);
- }
- GetProfiles(){
- return this.state.data.map((obj)=>{
- return (
- <div class="mycontainer" key={obj.id}>
- <h3>{obj.name}</h3>
- <a href={obj.url}>{obj.urlText}</a>;
- <button onClick ={()=> this.RemoveProfileHandler(obj.id)}>Remove </button>
- </div>
- );
- });
- }//end of GetProfiles
- render(){
- return (
- <div >
- {this.GetProfiles()}
- </div>
- );
- }
- }
- ReactDOM.render(<Profiles />,document.getElementById('app'));
- </script>
- </body>
- </html>

Join the conversation! Your thoughts help the community grow.