Description
copy and edited from #21930 (comment)
I will use the next 2 terms
- update call - execution of the function body(no effects)
- render cycle - update(s) call and following effects
2 very important notes:
- Calling state hook from effect(like useEffect or useLayoutEffect) will cause React to schedule another render.
- Calling state hook from FC body will cause React to schedule another update call.
on StrictMode:
after a small experiment,2 updates,1 render for each render cycle(means the body of the FC executes twice while effect fire once)
code
see the next code , and lets remember react triggers 2 updates(at least) per render on StrictMode, if the first update will trigger another update(via setState for example) it will come before the second update strictmode schedules:
if (shouldUpdatePosition.current) {
...
const pos = getPosition(...)
setSt(pos);
log('pos',pos)
shouldUpdatePosition.current = false;
}
log('st',st)
on the first render shouldUpdatePosition.current=true
so setSt gets the right position and update call scheduled by React with the right dimensions. the update call would execute and then on StrictMode, the second update would start(and the 3'th update call) and now shouldUpdatePosition.current=false
the setSt is not executed, and React takes the value from the first update and not from the previous update call. this is unexpected!
recap, and logs:
for logs explanation, let's say 0 is the wrong(not updated) value and 10 is the correct value
- update 0
'pos',10 (set state trigger, another update call is scheduled)
'st',0 - update 1
'st',10 - update 2 (StrictMode only! the second update call of strict mode!)
'st',0 (why? not expected! should be 10)
in order to fix this bug and make the result as expected react should take the value from the most recent update call and not the the first update call of the current cycle in strict mode
this is clearly a bug (or limitation) in StrictMode
code sandbox
see logs, this is not expected
https://codesandbox.io/s/react-strict-mode-bug-bx8is?file=/src/index.js