From 05f11a74a4a06b4abcb1e302f56201609acccad2 Mon Sep 17 00:00:00 2001 From: Aditya Sharat Date: Sat, 18 May 2019 04:24:40 -0700 Subject: [PATCH] Unset mComponentScope in the root ComponentContext. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: When a `ComponentTree` is initialized with a `ComponentContext` from another Component (from another tree) the global key of the root component is generated in context of a component which is outside the tree. This is generally `null`. In such a case when a state update is dispatched, and the framework tries to generate the global key for the root component. The root component (which has a non null parent in this case) will ask its parent to generate a global key. Now the **falsy** parent components child counters haven’t been reset so it will check that this is a new instance of the same component and deduplicate the global key (i.e. append !n). This changes the global keys for all descendants components. In some cases it can trigger a new state update and the cycle repeat over and over again. Reviewed By: davidaurelio Differential Revision: D15363542 fbshipit-source-id: bea8923e7087dbdedb430b3c1af5a47e688e4193 --- .../com/facebook/litho/ComponentContext.java | 1 + .../com/facebook/litho/ComponentTreeTest.java | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/litho-core/src/main/java/com/facebook/litho/ComponentContext.java b/litho-core/src/main/java/com/facebook/litho/ComponentContext.java index 75f32980cd7..c69784c6036 100644 --- a/litho-core/src/main/java/com/facebook/litho/ComponentContext.java +++ b/litho-core/src/main/java/com/facebook/litho/ComponentContext.java @@ -524,6 +524,7 @@ static ComponentContext withComponentTree(ComponentContext context, ComponentTre ComponentContext componentContext = new ComponentContext(context, new StateHandler(), null, null, null); componentContext.mComponentTree = componentTree; + componentContext.mComponentScope = null; componentContext.mLayoutStateFuture = null; return componentContext; diff --git a/litho-it/src/test/java/com/facebook/litho/ComponentTreeTest.java b/litho-it/src/test/java/com/facebook/litho/ComponentTreeTest.java index bcafee320f2..7615c57c40b 100644 --- a/litho-it/src/test/java/com/facebook/litho/ComponentTreeTest.java +++ b/litho-it/src/test/java/com/facebook/litho/ComponentTreeTest.java @@ -165,6 +165,16 @@ public void testCreate() { Assert.assertFalse(componentTreeHasSizeSpec(componentTree)); } + @Test + public void testCreate_ContextIsNotScoped() { + ComponentContext scopedContext = + ComponentContext.withComponentScope(mContext, Row.create(mContext).build()); + ComponentTree componentTree = ComponentTree.create(scopedContext, mComponent).build(); + + ComponentContext c = Whitebox.getInternalState(componentTree, "mContext"); + Assert.assertNull(c.getComponentScope()); + } + @Test public void testSetSizeSpec() { ComponentTree componentTree = @@ -202,6 +212,25 @@ public void testSetSizeSpecAsync() { postSizeSpecChecks(componentTree, "mBackgroundLayoutState"); } + @Test + public void testLayoutState_ContextIsNotScoped() { + ComponentContext scopedContext = + ComponentContext.withComponentScope(mContext, Row.create(mContext).build()); + Component root = Column.create(scopedContext).build(); + + ComponentTree componentTree = ComponentTree.create(scopedContext, root).build(); + + componentTree.setSizeSpecAsync(mWidthSpec, mHeightSpec); + + mLayoutThreadShadowLooper.runOneTask(); + + LayoutState layoutState = getInternalState(componentTree, "mBackgroundLayoutState"); + ComponentContext c = getInternalState(componentTree, "mContext"); + assertThat(c).isNotEqualTo(scopedContext); + Assert.assertNull(c.getComponentScope()); + assertThat(layoutState.getRootComponent().getScopedContext()).isNotEqualTo(scopedContext); + } + @Test public void testSetSizeSpecAsyncThenSyncBeforeRunningTask() { ComponentTree componentTree =