In this article,  we will learn more about the state and the set state method in react Js and also do's and don'ts of the state and set state method in React js with the best examples.

Do's and don'ts with state and setState()

Now, to help us understand the do's and don'ts with state and setState(), let us create a counter component. we will basically have a count value and a button to increment that counter value.

dos and donts of react state


Let me create a new component called counter.js so within the components folder a new file is counter.js. Since we are learning about the state in class components, this is also going to be a class component.

Let's create a class component called Counter, within the div tag, I'm going to have the text count, and then let me add the counter component in the App component, if you quickly take a look at the browser you should be able to see the text count.

Alright, let me create a counter component which you will see in the below counter.js file, the first thing we need is a count state to keep track of the counter value, and we initialize the state in the constructor, so the constructor a call to super and then this. state which is an object and the state object has a property called count initialized to zero.

Now, we can bind this state value with curly braces, so within the render method count - within curly braces this. state.count

Next, let's add a button to increment the count value, so I'm going to add parentheses to the return statement, add an enclosing div tag, move the div tag within the return statement and then add the button element. So button tag the text is going to be increment and we listen to the click event very similar to the last article. So on the opening button tag onClick attribute this is going to be equal to curly braces and an arrow function within the curly brace, again don't worry about the arrow function syntax, for now, just know that when you click on the button the increment method is called and of course, this has to be this dot increment.

Now let's define the increment method right after the constructor increment and if you can recollect, I mentioned in the last article, when we have to change the state of the component, we need to use the setState() method. But you might be curious as to what will happen if we don't use it and try to change the state directly let's try it out within the increment method this dot state dot count is equal to this dot state count plus 1, we are basically incrementing the count value by 1.  Let me simply log the updated count value in the console, so console.log,  this.state.count. So we are basically trying to fetch the current value of count incremented by 1 and we assign that value let's save this and take a look at the browser. 

Counter.js

import React, { Component } from 'react'

class Counter extends Component {
constructor(props) {
super(props)

this.state = {
count: 0

}
}

incremeent() {
this.state.count = this.state.count + 1

console.log(this.state.count)
}

render() {
return (
<div>
<div> Count - {this.state.count}</div>
<button onClick={() => this.incremeent()}></button>


</div>
)
}
}

App.js

import React from 'react'
import './App.css';
import Counter from './components/Counter'

function App() {
return (
<div className="App">
<Counter />

</div>
);
}

export default App;

Let me open the console and now I will click on the increment button. When I click you can see that the value is not incremented in the UI, in the console though you can see that, it has changed from 0 to 1, if I click again in the UI is still 0 but in the console, it is now 2 few more clicks and you can see that it's the same 0 in the UI but increments in the console.

Output:-



What this means is that the UI is not re-rendering whenever the state is changing and this is the main reason we should never modify the state directly.  The only place where you can assign this. state is the constructor, any other time to change the state setState method has to be used. So let's make the change and see if it helps, this. state accepts an object the count value is going to be this. state.count plus1.

If you now save this and take a look at the browser click increment button, you can see that the count value in the UI increments to 1, so the very first dos and don'ts with the state never modify the state directly, instead make use of setState() method.

Counter.js

import React, { Component } from 'react'

class Counter extends Component {
constructor(props) {
super(props)

this.state = {
count: 0

}
}

incremeent() {
this.setState({
count: this.state.count + 1
})
console.log(this.state.count)
}

render() {
return (
<div>
<div> Count - {this.state.count}</div>
<button onClick={() => this.incremeent()}></button>


</div>
)
}
}

export default Counter

Output:-

When you modify the state directly react will not rerender the component, setState() method on the other hand will let react know it has to re-render the component.

There is one detail to observe with the setState() method, in the browser you can see that when we click the increment button, the value is 1 but if you take a look at the console the value is 0

So the console value is less than the rendered value and this is because calls to setState() are asynchronous. 

So what is happening is, the console.log is being called before the state is actually set.  Many times in your application you might want to execute some code only after the state has been updated, to handle such a situation you can pass in a callback function as the second parameter to the setState() method.

So the setState() method has two parameters, the first parameter is the state object and the second parameter is the callback function. 

Counter.js

import React, { Component } from 'react'

class Counter extends Component {
constructor(props) {
super(props)

this.state = {
count: 0

}
}

incremeent() {
this.setState({
count: this.state.count + 1
},
() => {
console.log('Callback value', this.state.count)
})
console.log(this.state.count)
}

render() {
return (
<div>
<div> Count - {this.state.count}</div>
<button onClick={() => this.incremeent()}></button>


</div>
)
}
}

export default Counter

The callback function will be an arrow function and within the function body let's log to the console,

So we have one console.log value outside the setState() method and one within the callback function within the setState() method.

Now if we save this and take a look at the browser click on the button and in the console, you can see that we have a value of zero, and then callback value of one the same value of 1 is also rendered in the browser.

If you see the above counter.js file code, the 0 is from the synchronous console log statement and one is from the callback function console log statement. So this brings us to the second dos and don'ts. whenever you need to execute some code after the state has been changed, do not place that code right after the setState() method instead of placing that code within the callback function that is passed as a second parameter to the setState() method.

let's take a look at the next scenario, when we try to use the current state to calculate the new state value the code is working as expected, we don't see any problem the increment is working fine, that is simply because the current scenario is pretty simple

let's make the scenario slightly complicated, I'm going to create a new method called increment five and within the body, I simply call the increment method five times.

Definitely, a strange piece of code but it helps understand the better details of the setState() method also on click of the button you are going to call this. increment() method five times, so the count should increment from zero to five when we click on the button, let's save this and take a look at the browser.

Counter.js

import React, { Component } from 'react'

class Counter extends Component {
constructor(props) {
super(props)

this.state = {
count: 0

}
}

incremeent() {
this.setState({
count: this.state.count + 1
},
() => {
console.log('Callback value', this.state.count)
})
console.log(this.state.count)
}

incremeentFive() {
this.incremeent()
this.incremeent()
this.incremeent()
this.incremeent()
this.incremeent()
}

render() {
return (
<div>
<div> Count - {this.state.count}</div>
<button onClick={() => this.incremeentFive()}></button>


</div>
)
}
}

export default Counter

Output:-


When I click on the button, you can see that the value changes to one instead of changing to five, and in the console strangely zero is logged five times and even the callback value of one is logged five times.

Now, why is this behavior is because react may group multiple setState() method calls into a single update for better performance, so what happens in our scenario is that all the five setState() method calls are done in one single go and the updated value does not carry over between the different calls.

So whenever you have to update the state based on the previous state we need to pass a function as an argument to the setState() method instead of passing in an object.

Update increment method in the Counter.js file like below,

import React, { Component } from 'react'

class Counter extends Component {
constructor(props) {
super(props)

this.state = {
count: 0

}
}

incremeent() {
this.setState(prevState => ({
count: prevState.count + 1
}))
console.log(this.state.count)
}

incremeentFive() {
this.incremeent()
this.incremeent()
this.incremeent()
this.incremeent()
this.incremeent()
}

render() {
return (
<div>
<div> Count - {this.state.count}</div>
<button onClick={() => this.incremeentFive()}></button>


</div>
)
}
}

export default Counter

So very important you make note of this difference we are not using the current state instead we are always using the previous state this is going to give us the right results. let's save this go back to the browser click on increment and the count is displayed as 5 increments again and the count is 10 we are able to correctly render the UI based on this.

Conclusion

to quickly summarize,

1.  always make use of setState() and never modify the state directly.

2.  If a certain code has to be executed after the state has been updated place that code in the callback function which is the second argument to the setState() method.

3.  finally the third point is when you have to update the state based on the previous state value passing a function as an argument instead of the regular object.

So that is all about the setState() method in react. Thank you guys for reading this article 


Thank you for reading this article.  I'll see you guys in the next one.