React setState()
Tags: react, javascript, codenewbie
Published: 2021-01-05 20:28:48

As our cohort started learning about React, one of the first concepts we learned is the use of state and props. Without any prior knowledge, it was definitely mind boggling in the beginning. Of course it started sinking in after multiple lab practices. Hopefully you’ll find this blog helpful in making better sense of it. 

## State vs Props

Before we move forward, I wanted to differentiate State and Props because honestly it was the first thing I had trouble understanding. 

State is data maintained inside a specific component while Props is data passed in from a parent component. 

The main difference is which component owns that data. Props are read only and can only be updated by the child component IF a callback function is also passed down as props. The callback function will then trigger the upstream update of the parent’s component state. 

Now let’s talk about the function that will update the component’s state.

#### TLDR
	
>
> - `setState()` is asynchronous
> - pass a function instead of an object if you are doing some kind of calculations based on the actual current state
> - `setState()` can accept a callback function

## setState()

Using `setState()` is basically making an appointment to update the component’s state. I say appointment, because `setState()` is asynchronous. It means calling `setState()` will not update EXACTLY after you call it. Look at the example code below : 

```Javascript
import React, { Component } from 'react';

class App extends Component {

    state = { count : 0 }

    handleClick = () => {
        this.setState({count: this.state.count + 1 })
        this.setState({count: this.state.count + 1 })
        this.setState({count: this.state.count + 1 })
        console.log(this.state.count)
        // this.state.count logs 0 in the first click
        // this.state.count logs 1 after the second click
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>Add 3</button>
            </div>
        );
    }
}

export default App
```

You might expect that after clicking the button, `handleClick` will update the state and then `console.log` 3, but in fact you will get 0. Also clicking the button the second time will `console.log` 1 Why is this happening? Is it ignoring the other 2 `setState()` calls? It is not. 

React intentionally waits and batches `setState()` calls for better performance. The main reason behind this is because when a component’s state is altered, the component re-renders in response. This is important, let’s say a click of a button calls a `setState()` to a child component AND a `setState()` to it’s parent component, you don’t want the child to re-render twice. As your app becomes more complex, it can be very expensive and in turn might cause your browser to become unresponsive. 

#### prevState()

Keeping in mind of its asynchronous nature, this makes `this.state` unreliable. Going back to our previous example, each `setState()` call looks at this.state before it even gets updated. if you are updating state with values that depend on the current state pass a `function()` in `setState()` instead of an object.

Tweaking our code from above, look at the code below:

```Javascript
import React, { Component } from 'react';

class App extends Component {

    state = { count : 0 }

    handleClick = () => {
        this.setState(prevState => {
            return {count: prevState.count + 1 }})
        

        this.setState(prevState => {
            return {count: prevState.count + 1 }})
        

        this.setState(prevState => {
            return {count: prevState.count + 1 }})
        console.log(this.state.count)
        // this.state.count logs 0 in the first click
        // this.state.count logs 3 after the second click
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>Add 3</button>
            </div>
        );
    }
}

export default App
```

Clicking the button the first time will still `console.log` 0 but clicking it again the second time and the component re-renders, it will log 3. You can also use this if you’re updating an existing array or object in state by using the spread operator like so :

```Javascript
state = { arr : [{obj1}, {obj2}, {obj3}]  }

handleClick = () => {
    this.setState(prevState => {
      return {array: [...prevState.arr, {newObj}]}
    })
}
```
**sidenote:** 
> - The function above takes the the most updated `this.state.arr` 
> - Uses the `...` spread operator to make a copy of that arr
> - Adds a new object
> - Updates state using `setState()`

The benefit of using a function instead of an object gives us access to the most updated state and will queue the setState() calls so that they run in order. 

#### Callback()

Now you’re wondering, we’re still getting “0” after our first click.

Last but not least, how do you access the updated state after `setState()` actually does what it’s supposed to do? `setState()` can take a callback function. 

Last tweak at the function below: 

```Javascript
 state = { count : 0 }

    handleClick = () => {
        this.setState(prevState => {
            return {count: prevState.count + 3 }}, () => {
                console.log(this.state.count)
                // this.state.count logs 3
            })
            console.log(this.state.count)
            //this.state.count logs 0

    }
```
The second console.log is outside the scope therefore running before `setState()` finishes setting the state, which is why it logs 0. 

Basically the callback function runs AFTER `setState()` happens, therefore in this scope giving you access to the most updated state.

#### Conclusion

<p> I hope this somewhat made things clearer. Thank you so much for reading and please reach out to me if you any comments or suggestions.</p>