When to use callback function of setState in React

When I first read about setState in React documentation I said to myself “oh this is easy, this does one thing, change the state right? It’s like changing the value of a variable uh, ok…”

Then after that I read somewhere someone used callback of setState I simply ignore it, because I thought that’s just a fancy way of doing things, I want to stick to my initial thought about setState.

Some people even argued that, writing this

this.setState((prevState) => 
({ checked: !prevState.checked })
)

has better performance than doing this

this.setState({ checked: !this.state.checked })

In my opinion there isn’t much difference between two of them. The first used the callback which setState accept that as the second parameter, then returned the object to change the state.


When to use callback function of setState in React

I’d encounter an issue when I want to call a method of parent component in my children component. Maybe you’ll get it by looking at this code

handleMonthChange_next = () => {
this.setState({
currentMonth: +this.state.currentMonth + 1
})
    this.props.getCalendarData(this.state.currentMonth)
}

I want to do 2 things

i) change the state of the current children component

ii) pass a parameter then call getCalendarData method which is located in the parent component

Above code failed. Why? Because setState is asynchronous. getCalendarData function triggered before the currentMonth changed.

Hack it to make it work

handleMonthChange_next = () => {
this.setState({
currentMonth: +this.state.currentMonth + 1
})
    setTimeout(() => {
this.props.getCalendarData(this.state.currentMonth)
},100)
}

It worked but above code is just terrible. We cannot ensure the timing is always 100 ms delay for all of the devices.

Better practice

handleMonthChange_next = () => {
this.setState({
currentMonth: +this.state.currentMonth + 1
}, () => {
this.props.getCalendarData(this.state.currentMonth)
})
}

Put my getCalendarData in a callback, problem solved. Setstate has an optional second parameter which is a callback function!

Another way is to use componentDidUpdate, like this

componentDidUpdate(prevProps, prevState) {
if (this.state.currentMonth !== prevState.currentMonth) {
this.props.getCalendarData(this.state.currentMonth);
}
}

But I did not choose that way because my component is written in a pure functional way which allow re-usability.