Description
React version: 18.0.0
Steps To Reproduce
Normal example
The following example is normal. The value of count
is incremented by 1
when click the button.
The update of Code Snippet A
、Code Snippet B
and Code Snippet C
will be all batch. Because by the time the code executes to Code Snippet A
and Code Snippet C
, Code Snippet B
has already been added to the microtask queue. The flushSyncCallbacks
are executed in scheduleMicrotask
callback function, therefore, Code Snippet B
is executed first and then flushSyncCallbacks
is executed.
import { useEffect, useState, useRef } from "react";
export default function App() {
const [count, setCount] = useState(0);
const countRef = useRef(-1);
const handleClickButton = () => {
// No.B
Promise.resolve().then(() => {
setCount(countRef.current + 1);
});
// No.A
setCount(countRef.current + 1);
// No.C
setCount(countRef.current + 1);
// Code Snippet A、Code Snippet B and Code Snippet C are all batch processed
};
useEffect(() => {
countRef.current = count;
}, [count]);
return (
<div>
<p>count is: {count}</p>
{/* count will be incremented by 1 when click the button */}
<button onClick={handleClickButton}>增加</button>
</div>
);
}
link to code example: https://codesandbox.io/s/batch-update-in-native-xpwz33-forked-c4pii4?file=/src/App.js:0-581
Abnormal examples
The following example is abnormal. The value of count
is incremented by 2
when click the button.
The update of Code Snippet A
and Code Snippet C
will be batch, but the update ofCode Snippet B
won't be batch.
When the code executes to Code Snippet C
, Code Snippet B
has not been batch, although Code Snippet B
has been added to the microtask queue.
I think all updates added to the microtask queue should be batched when the code executes to the last update on the synchronous execution context.
import { useEffect, useState, useRef } from "react";
export default function App() {
const [count, setCount] = useState(0);
const countRef = useRef(-1);
const handleClickButton = () => {
// Code Snippet A
setCount(countRef.current + 1);
Promise.resolve().then(() => {
// Code Snippet B
setCount(countRef.current + 1);
});
// Code Snippet C
setCount(countRef.current + 1);
// Code Snippet A and Code Snippet C will be batch, but Code Snippet B won't be batch
};
useEffect(() => {
countRef.current = count;
}, [count]);
return (
<div>
{/* count will be incremented by 2 when click the button */}
<p>count is: {count}</p>
<button onClick={handleClickButton}>增加</button>
</div>
);
}
Link to code example: https://codesandbox.io/s/batch-update-in-native-xpwz33-xpwz33?file=/src/App.js:0-581
The current behavior
When a setState
is executed first in the synchronous execution context, all setState
in the microtask queue will not be batched, even if the synchronous execution context still has setState
after these microtasks.
The expected behavior
All updates added to the microtask queue should be batched when the code executes to the last update on the synchronous execution stack.