diff --git a/litho-core/src/main/java/com/facebook/litho/ComponentContextUtils.java b/litho-core/src/main/java/com/facebook/litho/ComponentContextUtils.java index e7d476e5a1a..c1ffc785679 100644 --- a/litho-core/src/main/java/com/facebook/litho/ComponentContextUtils.java +++ b/litho-core/src/main/java/com/facebook/litho/ComponentContextUtils.java @@ -87,7 +87,8 @@ private static LithoConfiguration mergeConfigurationWithNewLogTagAndLogger( logger != null ? logger : lithoConfiguration.logger, lithoConfiguration.renderUnitIdGenerator, lithoConfiguration.visibilityBoundsTransformer, - lithoConfiguration.debugEventListener); + lithoConfiguration.debugEventListener, + lithoConfiguration.isSpecsDuplicateStateUpdateDetectionEnabled); } public static LithoConfiguration buildDefaultLithoConfiguration( @@ -118,6 +119,7 @@ public static LithoConfiguration buildDefaultLithoConfiguration( loggerToUse, null, transformer, - null); + null, + ComponentsConfiguration.defaultInstance.specsApiStateUpdateDuplicateDetectionEnabled); } } diff --git a/litho-core/src/main/java/com/facebook/litho/ComponentTree.java b/litho-core/src/main/java/com/facebook/litho/ComponentTree.java index b9c081e86ed..799ffd8d65d 100644 --- a/litho-core/src/main/java/com/facebook/litho/ComponentTree.java +++ b/litho-core/src/main/java/com/facebook/litho/ComponentTree.java @@ -506,7 +506,8 @@ protected ComponentTree(Builder builder) { logger, renderUnitIdGenerator, builder.visibilityBoundsTransformer, - builder.componentTreeDebugEventListener); + builder.componentTreeDebugEventListener, + builder.config.specsApiStateUpdateDuplicateDetectionEnabled); if (ComponentsConfiguration.enableFixForNestedComponentTree) { mContext = @@ -1138,6 +1139,10 @@ public LithoConfiguration getLithoConfiguration() { return mContext.mLithoConfiguration; } + private boolean isSpecsDuplicateStateUpdateDetectionEnabled() { + return mContext.mLithoConfiguration.isSpecsDuplicateStateUpdateDetectionEnabled; + } + /** Returns whether incremental mount is enabled or not in this component. */ public boolean isIncrementalMountEnabled() { return mContext.mLithoConfiguration.incrementalMountEnabled; @@ -1308,7 +1313,12 @@ public void updateStateSync( if (mTreeState != null) { isStateEnqueued = - mTreeState.queueStateUpdate(componentKey, stateUpdate, false, isLayoutState); + mTreeState.queueStateUpdate( + componentKey, + stateUpdate, + false, + isLayoutState, + !isSpecsDuplicateStateUpdateDetectionEnabled()); } } @@ -1342,7 +1352,12 @@ public void updateStateAsync( if (mTreeState != null) { isStateUpdateEnqueued = - mTreeState.queueStateUpdate(componentKey, stateUpdate, false, isLayoutState); + mTreeState.queueStateUpdate( + componentKey, + stateUpdate, + false, + isLayoutState, + !isSpecsDuplicateStateUpdateDetectionEnabled()); } } diff --git a/litho-core/src/main/java/com/facebook/litho/LithoConfiguration.kt b/litho-core/src/main/java/com/facebook/litho/LithoConfiguration.kt index 522351f033b..c6fae548432 100644 --- a/litho-core/src/main/java/com/facebook/litho/LithoConfiguration.kt +++ b/litho-core/src/main/java/com/facebook/litho/LithoConfiguration.kt @@ -36,5 +36,6 @@ data class LithoConfiguration( @JvmField val logger: ComponentsLogger? = null, @JvmField val renderUnitIdGenerator: RenderUnitIdGenerator?, @JvmField val visibilityBoundsTransformer: VisibilityBoundsTransformer?, - @JvmField val debugEventListener: ComponentTreeDebugEventListener? + @JvmField val debugEventListener: ComponentTreeDebugEventListener?, + @JvmField val isSpecsDuplicateStateUpdateDetectionEnabled: Boolean ) {} diff --git a/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt b/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt index d3f5d02826e..9ccbb2801ec 100644 --- a/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt +++ b/litho-core/src/main/java/com/facebook/litho/config/ComponentsConfiguration.kt @@ -37,6 +37,11 @@ import com.facebook.rendercore.incrementalmount.IncrementalMountExtensionConfigs */ data class ComponentsConfiguration internal constructor( + /** + * This determines if the [ComponentTree] attached to this configuration, will attempt to detect + * and ignore duplicate state updates coming from usages in the Specs API. + */ + @JvmField val specsApiStateUpdateDuplicateDetectionEnabled: Boolean = false, val useCancellableLayoutFutures: Boolean = true, val useInterruptibleResolution: Boolean = true, val shouldCacheLayouts: Boolean = true, diff --git a/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/LithoTestRunner.java b/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/LithoTestRunner.java index 5067a5ea2c4..5cc3bfefaac 100644 --- a/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/LithoTestRunner.java +++ b/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/LithoTestRunner.java @@ -76,7 +76,8 @@ private List> getGlobalConf return Arrays.asList( LayoutCachingTestRunConfiguration.class, SkipRootCheckingTestRunConfiguration.class, - CustomBindersTestRunConfiguration.class); + CustomBindersTestRunConfiguration.class, + SpecsStateUpdateDetectionConfiguration.class); } @Override diff --git a/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/SpecsStateUpdateDetectionConfiguration.kt b/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/SpecsStateUpdateDetectionConfiguration.kt new file mode 100644 index 00000000000..c009b6b7d78 --- /dev/null +++ b/litho-testing/src/main/java/com/facebook/litho/testing/testrunner/SpecsStateUpdateDetectionConfiguration.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.litho.testing.testrunner + +import com.facebook.litho.config.ComponentsConfiguration +import org.junit.runners.model.FrameworkMethod + +class SpecsStateUpdateDetectionConfiguration : LithoTestRunConfiguration { + + private val defaultInstance = ComponentsConfiguration.defaultInstance + + override fun beforeTest(method: FrameworkMethod) { + ComponentsConfiguration.defaultInstance = + defaultInstance.copy(specsApiStateUpdateDuplicateDetectionEnabled = true) + } + + override fun afterTest(method: FrameworkMethod) { + ComponentsConfiguration.defaultInstance = defaultInstance + } +}