Description
React version: 18
Link to code example:
The expected behavior
Let's assume we have a increment
function that first increments a local value, then a uSES
value and then another local value like this:
function increment() {
setState1((x) => x + 1);
setUsesState((x) => x + 1);
setState2((x) => x + 1);
}
Now, there would be two ways this could behave that would be "intuitive for me":
- everything is batched:
[state1, usesState, state2]
goes from[0,0,0]
to[1,1,1]
- it batches until the "sync update" flushes the current batch:
[state1, usesState, state2]
goes from[0,0,0]
to[1,1,0]
to[1,1,1]
The current behavior
Now, actual behaviour is different in React 18, depending on the "mode" React is currently in.
- in an event handler, everything is batched
[0,0,0]
to[1,1,1]
- no problem here - outside an event handler, the
uSES
setter is flushed first, then the local state changes are batched.[0,0,0]
becomes[0,1,0]
becomes[1,1,1]
- this is very unintuitive for me. - even inside a manual call wrapped in
unstable_batchedUpdates
, we go[0,0,0]
->[0,1,0]
->[1,1,1]
Point 3 means that there is actually no way to even manually batch an update by uSES
- but looking at point 1, React sometimes does so internally.
It seems that even in the non-batched situations, React does some batching: Calling setUsesState
twice before calling setState2
will not lead to a [0,0,0]
-> [0,1,0]
-> [0,2,0]
-> [1,2,1]
situation, but to [0,0,0]
-> [0,2,0]
-> [1,2,1]
Up until now we had assumed that uSES
would always behave like in 1., and we were only made aware of this by bug reports on react-redux
.
Is this intended behaviour or a bug?
There might be some high priority update thing with a transition that I am missing here though - but either way this feels very breaking from older behaviour to me - and yes, I know that batchedUpdates
has the unstable
prefix ;)