diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
index ce7520ee9bd942..76749aaff7f948 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedModule.java
@@ -295,6 +295,16 @@ public void didScheduleMountItems(UIManager uiManager) {
mCurrentFrameNumber++;
}
+ @Override
+ public void willMountItems(UIManager uiManager) {
+ // noop
+ }
+
+ @Override
+ public void didMountItems(UIManager uiManager) {
+ // noop
+ }
+
// For FabricUIManager only
@Override
@UiThread
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java
index 984bcf15848ee8..505999b3d5af33 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/UIManagerListener.java
@@ -12,10 +12,33 @@ public interface UIManagerListener {
/**
* Called right before view updates are dispatched at the end of a batch. This is useful if a
* module needs to add UIBlocks to the queue before it is flushed.
+ *
+ *
This is called by Paper only.
*/
void willDispatchViewUpdates(UIManager uiManager);
- /* Called right after view updates are dispatched for a frame. */
+ /**
+ * Called on UIThread right before view updates are executed.
+ *
+ *
This is called by Fabric only.
+ */
+ void willMountItems(UIManager uiManager);
+ /**
+ * Called on UIThread right after view updates are executed.
+ *
+ *
This is called by Fabric only.
+ */
+ void didMountItems(UIManager uiManager);
+ /**
+ * Called on UIThread right after view updates are dispatched for a frame. Note that this will be
+ * called for every frame even if there are no updates.
+ *
+ *
This is called by Fabric only.
+ */
void didDispatchMountItems(UIManager uiManager);
- /* Called right after scheduleMountItems is called in Fabric, after a new tree is committed. */
+ /**
+ * Called right after scheduleMountItems is called in Fabric, after a new tree is committed.
+ *
+ *
This is called by Fabric only.
+ */
void didScheduleMountItems(UIManager uiManager);
}
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 0da8e55dd8d938..8e50e91a64d52e 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
@@ -1171,6 +1171,20 @@ public Map getPerformanceCounters() {
}
private class MountItemDispatchListener implements MountItemDispatcher.ItemDispatchListener {
+ @Override
+ public void willMountItems() {
+ for (UIManagerListener listener : mListeners) {
+ listener.willMountItems(FabricUIManager.this);
+ }
+ }
+
+ @Override
+ public void didMountItems() {
+ for (UIManagerListener listener : mListeners) {
+ listener.didMountItems(FabricUIManager.this);
+ }
+ }
+
@Override
public void didDispatchMountItems() {
for (UIManagerListener listener : mListeners) {
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java
index 36abe8dd9f15cf..6861961c472640 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java
@@ -192,6 +192,8 @@ private boolean dispatchMountItems() {
return false;
}
+ mItemDispatchListener.willMountItems();
+
// As an optimization, execute all ViewCommands first
// This should be:
// 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always
@@ -298,6 +300,9 @@ private boolean dispatchMountItems() {
}
mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime;
}
+
+ mItemDispatchListener.didMountItems();
+
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
return true;
@@ -414,6 +419,10 @@ private static void printMountItem(MountItem mountItem, String prefix) {
}
public interface ItemDispatchListener {
+ void willMountItems();
+
+ void didMountItems();
+
void didDispatchMountItems();
}
}
diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java
index 6ce4d8b23d2bbf..f76c432b59047c 100644
--- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java
+++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java
@@ -18,6 +18,7 @@
import com.facebook.react.bridge.UIManagerListener;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.uimanager.UIManagerHelper;
+import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.common.ViewUtil;
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll;
import com.facebook.react.views.view.ReactViewGroup;
@@ -89,6 +90,14 @@ public void stop() {
* been updated.
*/
public void updateScrollPosition() {
+ // On Fabric this will be called internally in `didMountItems`.
+ if (ViewUtil.getUIManagerType(mScrollView.getId()) == UIManagerType.FABRIC) {
+ return;
+ }
+ updateScrollPositionInternal();
+ }
+
+ private void updateScrollPositionInternal() {
if (mConfig == null || mFirstVisibleView == null || mPrevFirstVisibleFrame == null) {
return;
}
@@ -169,6 +178,16 @@ public void run() {
});
}
+ @Override
+ public void willMountItems(UIManager uiManager) {
+ computeTargetView();
+ }
+
+ @Override
+ public void didMountItems(UIManager uiManager) {
+ updateScrollPositionInternal();
+ }
+
@Override
public void didDispatchMountItems(UIManager uiManager) {
// noop