diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 74204078debd..64d9655cba59 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -101,6 +101,7 @@ import java.util.Map; import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -192,6 +193,13 @@ public class FabricUIManager @ThreadConfined(UI) private final Set mSynchronousEvents = new HashSet<>(); + /** + * Queue of surface IDs that need their React revision merged. Drained during doFrame so that + * synchronous events dispatched by the merge are processed in the same frame. + */ + private final ConcurrentLinkedQueue mPendingReactRevisionMerges = + new ConcurrentLinkedQueue<>(); + /** * This is used to keep track of whether or not the FabricUIManager has been destroyed. Once the * Catalyst instance is being destroyed, we should cease all operation here. @@ -956,13 +964,7 @@ private void scheduleReactRevisionMerge(int surfaceId) { mBinding.mergeReactRevision(surfaceId); } } else { - UiThreadUtil.runOnUiThread( - () -> { - FabricUIManagerBinding binding = mBinding; - if (binding != null) { - binding.mergeReactRevision(surfaceId); - } - }); + mPendingReactRevisionMerges.add(surfaceId); } } @@ -1558,6 +1560,18 @@ public void doFrameGuarded(long frameTimeNanos) { return; } + // Drain pending React revision merges first so that animations, + // preallocation, and mount items operate against the latest revision. + if (ReactNativeFeatureFlags.enableFabricCommitBranching()) { + FabricUIManagerBinding binding = mBinding; + if (binding != null) { + Integer mergeSurfaceId; + while ((mergeSurfaceId = mPendingReactRevisionMerges.poll()) != null) { + binding.mergeReactRevision(mergeSurfaceId); + } + } + } + // Drive any animations from C++. // There is a race condition here between getting/setting // `mDriveCxxAnimations` which shouldn't matter; it's safe to call