From 27c9c95e23ddedb9163373950e364dd62038f6c0 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 23 Jun 2021 20:32:44 -0400 Subject: [PATCH] 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. --- packages/react-reconciler/src/ReactFiberWorkLoop.new.js | 9 ++++++++- packages/react-reconciler/src/ReactFiberWorkLoop.old.js | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index b21c7b2e33f90..6ce1337299794 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -692,7 +692,14 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { } if (supportsMicrotasks) { // Flush the queue in a microtask. - scheduleMicrotask(flushSyncCallbacks); + if (__DEV__ && ReactCurrentActQueue.current !== null) { + // Inside `act`, use our internal `act` queue so that these get flushed + // at the end of the current scope even when using the sync version + // of `act`. + ReactCurrentActQueue.current.push(flushSyncCallbacks); + } else { + scheduleMicrotask(flushSyncCallbacks); + } } else { // Flush the queue in an Immediate task. scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 399ea10393ef5..b99245f097d17 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -692,7 +692,14 @@ function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { } if (supportsMicrotasks) { // Flush the queue in a microtask. - scheduleMicrotask(flushSyncCallbacks); + if (__DEV__ && ReactCurrentActQueue.current !== null) { + // Inside `act`, use our internal `act` queue so that these get flushed + // at the end of the current scope even when using the sync version + // of `act`. + ReactCurrentActQueue.current.push(flushSyncCallbacks); + } else { + scheduleMicrotask(flushSyncCallbacks); + } } else { // Flush the queue in an Immediate task. scheduleCallback(ImmediateSchedulerPriority, flushSyncCallbacks);