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>
}
- Before pressing the button
console
just printsnull
- First time button is pressed
console
printsfoo
- Second time button is pressend
console
printfoo
- 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.