Why React needs another render to bail out state updates?

Consider the following Component

const Component = () =>{
    const [state, setState] = useState(null)

    const onClick = () => setState('foo')        

    console.log(state)

    return <button onClick={onClick}> Change </button>   
}

Edit sad-thompson-s3l3i

  • Before pressing the button console just prints null
  • First time button is pressed console prints foo
  • Second time button is pressend console print foo
  • Third time and forward console doesn't print anything

I understand that console doesn't print anything cause I'm calling setState passing the same value as the current state and React is bailing out the state update. My question is about the following assertion

Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with useMemo.

Why is this extra render necessary? I mean, Isn't Object.is returning false since the second click?


Solution 1:

Internally useState is a useReducer, with a basicReducer, The hooks uses a queue of changes in order to update the states.

AFAIK after looking the code it is a condition where the memoizedState is not fully processed in the queue, because is no using the fine control of the hook useMemo

Solution 2:

I got a satisfatory answer from a deleted answer in this same question. d.c pointed that Dan Abrahmov gave an answer in this old github issue. Quoting Dan:

I think at the time we decided that we don't actually know if it's safe to bail out in all cases until we try render again. The "bailout" here means that it doesn't go ahead to render children. But re-running the same function might be necessary (for example, if a reducer is inline and we don't know if we bail out until we re-run the reducer on next render). So for consistency we always re-run it on updates during the render phase.