componentDidMount(){
setTimeout(() => {
this.setState({ count: 1 }, () => {
console.log(`banana`)
})
console.log(`lemen`)
setTimeout(() => {
console.log(`grape`)
}, 0)
this.setState({ count: 2 }, () => {
console.log(`strawberry`)
})
console.log(`pear`)
}, 0)
}
Why did lemen print behind banana?
@gaearon had an explanation for this on StackOverflow recently, but I can't find the comment. If I remember correctly (I think I do), setState is only async batched when it is called inside a React event handler, otherwise it is sync. Calling setState in setTimeout is therefore sync. I can confirm this behavior from my experience. This is not a bug, but it will probably change in the future (this was also mentioned in the issue I base my answer on).
I am not part of the React team, correct me if I wrote something stupid.
link to explanation -> https://github.com/facebook/react/issues/11527#issuecomment-360199710
@gaearon had an explanation for this on StackOverflow recently, but I can't find the comment. If I remember correctly (I think I do),
setStateis only async batched when it is called inside a React event handler, otherwise it is sync. CallingsetStateinsetTimeoutis therefore sync. I can confirm this behavior from my experience. This is not a bug, but it will probably change in the future (this was also mentioned in the issue I base my answer on).I am not part of the React team, correct me if I wrote something stupid.
If I call setState inside a event handler added with addEventHandler, it's also sync.
How can setState know it was called inside a React event handler?
@aztack you pass the event handlers to React and React is calling them when needed. From then on there are multiple ways to know if setState was called during the handler, the simplest one is this:
let isHandlerRunning = false
// handler is something you passed to onClick for example
function runHandler (handler) {
isHandlerRunning = true
handler()
isHandlerRunning = false
}
// this is a setState implementation shell
function setState () {
if (isHandlerRunning) {
// do batching here
} else {
// do immediate update here
}
}
This is called sync batching and implementing this requires the framework to have full control over the callback executions (it has to be the one calling the callbacks - like event handlers). React has control over the jsx event handlers but has no control over native event handlers or window timers. Sync batching those would require monkey patching (if I know correctly Angular zones do exactly this).
I am not into the React source code so the above snippets are just possible implementations. I can not guarantee that the React implementation looks like them.
Thank you @solkimicreb . I think you are right. I'll dig into React source code and see if I can find something like that.
Most helpful comment
@gaearon had an explanation for this on StackOverflow recently, but I can't find the comment. If I remember correctly (I think I do),
setStateis only async batched when it is called inside a React event handler, otherwise it is sync. CallingsetStateinsetTimeoutis therefore sync. I can confirm this behavior from my experience. This is not a bug, but it will probably change in the future (this was also mentioned in the issue I base my answer on).I am not part of the React team, correct me if I wrote something stupid.