0

So here is Piece of Code for onClick EventHandler in React

code :

function handleChange(event) {
console.log('before 1st update')

setCount(prevCount => {
  console.log('inside 1st update')
  return prevCount + 1
})

console.log('After 1st update')

setCount(prevCount => {
  console.log('inside 2nd update')
  return prevCount + 1
})

console.log('After 2nd update')}

Output :

before 1st update
inside 1st update
After 1st update
After 2nd update
inside 2nd update

Expected Output :

before 1st update
inside 1st update
After 1st update
inside 2nd update
After 2nd update

Could Someone Explain? Also, The example provides decent enough evidence that the updater function is synchronous and updation is asynchronous(in the case of Browser Events).

Tudiman555
  • 35
  • 6
  • The functions you put in `setState` don't happen automatically - there is asynchronicity to how they're run. This doesn't mean that a second `setState` could run before the first call, however, since they're queued so order will be preserved. – Samathingamajig Mar 01 '22 at 15:33
  • The looks like a [XY-problem](https://xyproblem.info). You should not rely on the execution timing of the `useState` setter callback. Why is this an issue? Could you give us the scenario where this becomes a problem? – 3limin4t0r Mar 01 '22 at 15:39
  • `setState` is sort of asynchronous because state requests are queued. This means that they are not executed in place unless you break up the queue batching. If you make this function `async` and trigger an `await` call between these two `setState`s, you will have them executed independently. – OFRBG Mar 01 '22 at 15:40

1 Answers1

2

setState in React acts like an async function.
So putting a console.log(state) right after setting it, will most likely show the former value, as it doesn't actually finish updating the state until the log command runs.
What we can do to act upon a change in state, is use React's built-in useEffect hook that has the relevant state as a dependency to check the value.

Example:

useEffect(() => {
   console.log(state);
}, [state]);

Basically, the callback function in the example will run every time the state changes.

In your case, it should look like this:

function handleChange(event) {
   console.log('before 1st update')

   setCount(prevCount => {
      console.log('inside 1st update')
      return prevCount + 1
   })

   setCount(prevCount => {
      console.log('inside 2nd update')
      return prevCount + 1
   })
}

useEffect(() => {
   console.log('count has been updated:', count)
}, [count])

The rest of the logs are valid.

tomleb
  • 2,409
  • 2
  • 9
  • 24
  • Could you explain why "After 2nd update" did not log as "After 1st Update" i.e after the respective update function? – Tudiman555 Mar 02 '22 at 13:38
  • I believe it has to do with React trying to prevent you from "shooting yourself in the foot" by detecting that you are trying to set the same state piece twice and therefor delaying the second attempt. – tomleb Mar 02 '22 at 14:12
  • 1
    Think about it this way, due to the asynchronous nature of `setState`, if you were to try and set the same state piece twice in a row, without any guards in place, it will likely result in a [race-condition](https://stackoverflow.com/a/34550), and therefor, in some cases, the first `setState` might actually finish **after** the second one, resulting in that second `setState` to effictively not even work. It is a little tricky to explain but I hope it makes sense. – tomleb Mar 02 '22 at 14:14
  • So it's like this, if I understood you correctly, clickHandler() { 1: setState(someFunction()) 2: setState(someFunction()) 3 : other piece of Code } So 2-second line is delayed intentionally(this is React's Doing) while other 1 and 3 executes in one go, then 2nd setState is executed afterwards. – Tudiman555 Mar 03 '22 at 12:12
  • Similarly, if i add another setState() after 2nd one so they will be batched accordingly – Tudiman555 Mar 03 '22 at 12:13
  • 1
    Yes, I believe that is the case. Note that it might not be entirely accurate as I did not pull this information from a verified source, I couldn't find anything specific that discusses this exact matter (whether or not React has this type of "guard" in place or if it just naturally behaves like this). My answer is based on my current experience with React following about a year of work in the field. – tomleb Mar 03 '22 at 12:37