diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index d72e6cc8b..a5a719cc1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -10,7 +10,6 @@ package com.facebook.react.fabric; import static android.view.View.MeasureSpec.AT_MOST; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.UNSPECIFIED; -import static com.facebook.react.uimanager.common.UIManagerType.DEFAULT; import static com.facebook.react.uimanager.common.UIManagerType.FABRIC; import android.os.SystemClock; @@ -22,7 +21,6 @@ import com.facebook.infer.annotation.Assertions; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.JavaScriptContextHolder; import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableNativeMap; @@ -41,13 +39,11 @@ import com.facebook.react.uimanager.ReactShadowNode; import com.facebook.react.uimanager.ReactShadowNodeImpl; import com.facebook.react.uimanager.ReactStylesDiffMap; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerHelper; import com.facebook.react.uimanager.UIViewOperationQueue; import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewManagerRegistry; import com.facebook.react.uimanager.common.MeasureSpecProvider; import com.facebook.react.uimanager.common.SizeMonitoringFrameLayout; -import com.facebook.react.uimanager.common.ViewUtil; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; @@ -295,10 +291,10 @@ public class FabricUIManager implements UIManager, JSHandler { "FabricUIManager.appendChild") .flush(); try { - // If the child to append is shared with another tree (child.getParent() != null), + // If the child to append was already committed (child.isSealed()), // then we add a mutation of it. In the future this will be performed by FabricJS / Fiber. //TODO: T27926878 avoid cloning shared child - if (child.getParent() != null) { + if (child.isSealed()) { child = child.mutableCopy(child.getInstanceHandle()); } parent.addChildAt(child, parent.getChildCount()); @@ -474,6 +470,7 @@ public class FabricUIManager implements UIManager, JSHandler { // and we do not need to hold references to the previous tree anymore node.setOriginalReactShadowNode(null); node.markUpdateSeen(); + node.markAsSealed(); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java index ed134e216..3f78d8af5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java @@ -377,4 +377,16 @@ public interface ReactShadowNode { long getInstanceHandle(); void setInstanceHandle(long instanceHandle); + + /** + * Mark this {@link ReactShadowNode} as sealed. This means that the node was already committed + * and it should not be updated anymore. + */ + void markAsSealed(); + + /** + * @return a {@link boolean} that represents if the {@link ReactShadowNode} is sealed. + */ + boolean isSealed(); + } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java index 6a334c3a7..dffd37266 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java @@ -128,6 +128,7 @@ public class ReactShadowNodeImpl implements ReactShadowNode private @Nullable ReactStylesDiffMap mNewProps; private long mInstanceHandle; + private boolean mIsSealed = false; public ReactShadowNodeImpl() { mDefaultPadding = new Spacing(0); @@ -164,6 +165,7 @@ public class ReactShadowNodeImpl implements ReactShadowNode mNewProps = null; mParent = null; mOriginalReactShadowNode = original; + mIsSealed = false; } private void replaceChild(ReactShadowNodeImpl newNode, int childIndex) { @@ -1147,4 +1149,14 @@ public class ReactShadowNodeImpl implements ReactShadowNode public void setInstanceHandle(long instanceHandle) { mInstanceHandle = instanceHandle; } + + @Override + public void markAsSealed() { + mIsSealed = true; + } + + @Override + public boolean isSealed() { + return mIsSealed; + } } diff --git a/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerTest.java b/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerTest.java index ba6b10c8c..ee7e96fb2 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerTest.java @@ -233,6 +233,24 @@ public class FabricUIManagerTest { mFabricUIManager.completeRoot(rootTag, children); } + @Test + public void testSealReactShadowNode() { + ReactRootView rootView = + new ReactRootView(RuntimeEnvironment.application.getApplicationContext()); + int rootTag = mFabricUIManager.addRootView(rootView); + String viewClass = ReactViewManager.REACT_CLASS; + + ReactShadowNode container = mFabricUIManager.createNode(6, viewClass, rootTag, null, randomInstanceHandle()); + List childSet = mFabricUIManager.createChildSet(rootTag); + mFabricUIManager.appendChildToSet(childSet, container); + + assertThat(container.isSealed()).isFalse(); + + mFabricUIManager.completeRoot(rootTag, childSet); + + assertThat(container.isSealed()).isTrue(); + } + /** * Tests that cloned text nodes will not share measure functions */