-
Notifications
You must be signed in to change notification settings - Fork 47.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug: Updates within microtasks are sometimes not batch #24365
Comments
Also having the same issue with setState and useReducer's dispatch. We use a lot of async functions so definitely a reason to downgrade back to react v17. |
This comment was marked as off-topic.
This comment was marked as off-topic.
In order to be able to help you, we need to know more. Can you post an example of how the current behavior actually breaks your code? It's not obvious how what you're describing is related to this issue, or even what you are describing exactly. A small sandbox that shows what exactly the breakage is would be very helpful. In this issue, @zh-lx documented an inconsistency in when batching is applied or not applied. While we'd like to fix it, it's not clear to me why this inconsistency would actually cause a problem for your code. Comments like "definitely a reason to downgrade back" are especially not helpful since if you don't provide more details, we can't solve the issue that prevents you from upgrading. |
I don't think so. The existing model is logical. |
The main problem is that I'm dispatching actions with useReducer (inside an async callback) and the reducer code is never executed. Will try to create a basic sample. |
Ok found out the issue, maybe it's not related to the initial bug described in this ticket. We run the react app inside a native app's WebView on mobile devices. To communicate between the native app and JavaScript Coherent UI is used. For this a new iframe is created and destroyed again. Somehow the batched state changes are not flushed if the promise inside changes the DOM (= attaching an iframe). Sample code: async function sendIframe() {
return new Promise(resolve => {
const document = window.document;
const frame = document.createElement('iframe');
document.documentElement.appendChild(frame);
document.documentElement.removeChild(frame);
setTimeout(() => resolve(true), 2000);
}
);
}
const App = () => {
const [showSpinner, setShowSpinner] = useState(false);
const process = React.useCallback(async() => {
setShowSpinner(true);
console.log('start');
// This method roughly resembles a Coherent UI communication.
// Both state changes (setShowSpinner) are never executed.
await sendIframe();
setShowSpinner(false);
console.log('done');
}, []);
const renderSpinner = useCallback(() => {
return (
<div>Spinner</div>
);
}, []);
return (
<>
{showSpinner && renderSpinner()}
<button onClick={() => process()}>Upload</button>
</>
);
};
const rootElement = document.getElementById('app');
const root = createRoot(rootElement);
root.render(<App />); |
Would you mind filing a separate issue for this with a runnable example? |
I'd love to. You can compare the two working examples below. In this case, clicking the button once increases the value of count by one: Promise isn't batched example In this case, clicking the button once increases the value of count by two: Promise is batched example |
This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment! |
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you! |
React version: 18.0.0
Steps To Reproduce
Normal example
The following example is normal. The value of
count
is incremented by1
when click the button.The update of
Code Snippet A
、Code Snippet B
andCode Snippet C
will be all batch. Because by the time the code executes toCode Snippet A
andCode Snippet C
,Code Snippet B
has already been added to the microtask queue. TheflushSyncCallbacks
are executed inscheduleMicrotask
callback function, therefore,Code Snippet B
is executed first and thenflushSyncCallbacks
is executed.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 by2
when click the button.The update of
Code Snippet A
andCode 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, althoughCode 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.
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, allsetState
in the microtask queue will not be batched, even if the synchronous execution context still hassetState
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.
The text was updated successfully, but these errors were encountered: