act: Bypass microtask for "default sync" updates (#21740)
When wrapping an update in act, instead of scheduling a microtask, we can add the task to our internal queue. The benefit is that the user doesn't have to await the act call. We can flush the work synchronously. This doesn't account for microtasks that are scheduled in userspace, of course, but it at least covers React's usage.
Andrew Clark committed
Jun 23, 2021 at 20:32 UTC
27c9c95e23ddedb9163373950e364dd62038f6c0
2 files changed
+16
-2
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+8
-1
@@ -692,7 +692,14 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
692
}
693
if (supportsMicrotasks) {
694
// Flush the queue in a microtask.
695
- scheduleMicrotask(flushSyncCallbacks);
695
+ if (__DEV__ && ReactCurrentActQueue.current !== null) {
696
+ // Inside `act`, use our internal `act` queue so that these get flushed
697
+ // at the end of the current scope even when using the sync version
698
+ // of `act`.
699
+ ReactCurrentActQueue.current.push(flushSyncCallbacks);
700
+ } else {
701
+ scheduleMicrotask(flushSyncCallbacks);
702
+ }
703
} else {
704
// Flush the queue in an Immediate task.
705
scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+8
-1
@@ -692,7 +692,14 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
692
}
693
if (supportsMicrotasks) {
694
// Flush the queue in a microtask.
695
- scheduleMicrotask(flushSyncCallbacks);
695
+ if (__DEV__ && ReactCurrentActQueue.current !== null) {
696
+ // Inside `act`, use our internal `act` queue so that these get flushed
697
+ // at the end of the current scope even when using the sync version
698
+ // of `act`.
699
+ ReactCurrentActQueue.current.push(flushSyncCallbacks);
700
+ } else {
701
+ scheduleMicrotask(flushSyncCallbacks);
702
+ }
703
} else {
704
// Flush the queue in an Immediate task.
705
scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);