Add RN feature flag for ScrollView view recycling (#53373)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53373

# Changelog:
[Internal] -

Creates a RN feature flag for enabling/disabling view recycling for ScrollView native component on Android.

Reviewed By: lenaic

Differential Revision: D80625656

fbshipit-source-id: 23eae07512e0ba2ea014404e7afa19db900f7c47
This commit is contained in:
Ruslan Shestopalyuk
2025-08-21 09:47:23 -07:00
committed by Facebook GitHub Bot
parent c2d96d3a5b
commit bcf76e8bb0
20 changed files with 166 additions and 49 deletions
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f162c19a6742ecadd171500ccb918f4b>>
* @generated SignedSource<<1e8a84a53072fa0c8665aead80d0199f>>
*/
/**
@@ -258,6 +258,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableViewRecycling(): Boolean = accessor.enableViewRecycling()
/**
* Enables View Recycling for <ScrollView> via ReactViewGroup/ReactViewManager.
*/
@JvmStatic
public fun enableViewRecyclingForScrollView(): Boolean = accessor.enableViewRecyclingForScrollView()
/**
* Enables View Recycling for <Text> via ReactTextView/ReactTextViewManager.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<51bb91fd70ba266c01bf46b3ca237ad5>>
* @generated SignedSource<<d07316109dce5cc1ce7b0dc010962c3c>>
*/
/**
@@ -58,6 +58,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableResourceTimingAPICache: Boolean? = null
private var enableViewCullingCache: Boolean? = null
private var enableViewRecyclingCache: Boolean? = null
private var enableViewRecyclingForScrollViewCache: Boolean? = null
private var enableViewRecyclingForTextCache: Boolean? = null
private var enableViewRecyclingForViewCache: Boolean? = null
private var enableVirtualViewDebugFeaturesCache: Boolean? = null
@@ -430,6 +431,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableViewRecyclingForScrollView(): Boolean {
var cached = enableViewRecyclingForScrollViewCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableViewRecyclingForScrollView()
enableViewRecyclingForScrollViewCache = cached
}
return cached
}
override fun enableViewRecyclingForText(): Boolean {
var cached = enableViewRecyclingForTextCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d4a1d4510264580e2f1436b05f1c2f0e>>
* @generated SignedSource<<6e384a07f0e7bc237f72b49a1268e16b>>
*/
/**
@@ -104,6 +104,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableViewRecycling(): Boolean
@DoNotStrip @JvmStatic public external fun enableViewRecyclingForScrollView(): Boolean
@DoNotStrip @JvmStatic public external fun enableViewRecyclingForText(): Boolean
@DoNotStrip @JvmStatic public external fun enableViewRecyclingForView(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<69634efaec0f2c63ac2d1ce20dd5c2ac>>
* @generated SignedSource<<19022bb7e93ed79a1cbe86c71f34bd50>>
*/
/**
@@ -99,6 +99,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableViewRecycling(): Boolean = false
override fun enableViewRecyclingForScrollView(): Boolean = true
override fun enableViewRecyclingForText(): Boolean = true
override fun enableViewRecyclingForView(): Boolean = true
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<78c9ee8f53273560de6b75719e317273>>
* @generated SignedSource<<1a19e0569371a038ba8d3849fb26eb5c>>
*/
/**
@@ -62,6 +62,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableResourceTimingAPICache: Boolean? = null
private var enableViewCullingCache: Boolean? = null
private var enableViewRecyclingCache: Boolean? = null
private var enableViewRecyclingForScrollViewCache: Boolean? = null
private var enableViewRecyclingForTextCache: Boolean? = null
private var enableViewRecyclingForViewCache: Boolean? = null
private var enableVirtualViewDebugFeaturesCache: Boolean? = null
@@ -472,6 +473,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableViewRecyclingForScrollView(): Boolean {
var cached = enableViewRecyclingForScrollViewCache
if (cached == null) {
cached = currentProvider.enableViewRecyclingForScrollView()
accessedFeatureFlags.add("enableViewRecyclingForScrollView")
enableViewRecyclingForScrollViewCache = cached
}
return cached
}
override fun enableViewRecyclingForText(): Boolean {
var cached = enableViewRecyclingForTextCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<3db9f160e7221eb9c7660a3e93da0f40>>
* @generated SignedSource<<5f84a9a17209b9ecb95e13e6567e7c3c>>
*/
/**
@@ -99,6 +99,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableViewRecycling(): Boolean
@DoNotStrip public fun enableViewRecyclingForScrollView(): Boolean
@DoNotStrip public fun enableViewRecyclingForText(): Boolean
@DoNotStrip public fun enableViewRecyclingForView(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0dadaee3f6bd74cc13295b8683e8e470>>
* @generated SignedSource<<1eff5bade524e3ad8e827ad4adb37f1a>>
*/
/**
@@ -267,6 +267,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableViewRecyclingForScrollView() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableViewRecyclingForScrollView");
return method(javaProvider_);
}
bool enableViewRecyclingForText() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableViewRecyclingForText");
@@ -635,6 +641,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableViewRecycling(
return ReactNativeFeatureFlags::enableViewRecycling();
}
bool JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForScrollView(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableViewRecyclingForScrollView();
}
bool JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForText(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableViewRecyclingForText();
@@ -925,6 +936,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableViewRecycling",
JReactNativeFeatureFlagsCxxInterop::enableViewRecycling),
makeNativeMethod(
"enableViewRecyclingForScrollView",
JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForScrollView),
makeNativeMethod(
"enableViewRecyclingForText",
JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForText),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<74ac256afe63253ddb6576285858444c>>
* @generated SignedSource<<05dba4cd49bd4f490e1ea943dd02cae0>>
*/
/**
@@ -144,6 +144,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableViewRecycling(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableViewRecyclingForScrollView(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableViewRecyclingForText(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<392fdd6e857b7c8ae99329b927be6ae0>>
* @generated SignedSource<<a7bd9fcbbcb19ee8a939b4c08e0fd508>>
*/
/**
@@ -178,6 +178,10 @@ bool ReactNativeFeatureFlags::enableViewRecycling() {
return getAccessor().enableViewRecycling();
}
bool ReactNativeFeatureFlags::enableViewRecyclingForScrollView() {
return getAccessor().enableViewRecyclingForScrollView();
}
bool ReactNativeFeatureFlags::enableViewRecyclingForText() {
return getAccessor().enableViewRecyclingForText();
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b601a3c92cd80549ca2391ab0a123214>>
* @generated SignedSource<<4831f7b871223d8347db93f9a0b3a1f9>>
*/
/**
@@ -229,6 +229,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableViewRecycling();
/**
* Enables View Recycling for <ScrollView> via ReactViewGroup/ReactViewManager.
*/
RN_EXPORT static bool enableViewRecyclingForScrollView();
/**
* Enables View Recycling for <Text> via ReactTextView/ReactTextViewManager.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<69518331e8b6225d4aa524e46951302d>>
* @generated SignedSource<<d5243da4eb6bdde1e703ae55fcccbf0f>>
*/
/**
@@ -713,6 +713,24 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() {
auto flagValue = enableViewRecyclingForScrollView_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(38, "enableViewRecyclingForScrollView");
flagValue = currentProvider_->enableViewRecyclingForScrollView();
enableViewRecyclingForScrollView_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() {
auto flagValue = enableViewRecyclingForText_.load();
@@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(38, "enableViewRecyclingForText");
markFlagAsAccessed(39, "enableViewRecyclingForText");
flagValue = currentProvider_->enableViewRecyclingForText();
enableViewRecyclingForText_ = flagValue;
@@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(39, "enableViewRecyclingForView");
markFlagAsAccessed(40, "enableViewRecyclingForView");
flagValue = currentProvider_->enableViewRecyclingForView();
enableViewRecyclingForView_ = flagValue;
@@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(40, "enableVirtualViewDebugFeatures");
markFlagAsAccessed(41, "enableVirtualViewDebugFeatures");
flagValue = currentProvider_->enableVirtualViewDebugFeatures();
enableVirtualViewDebugFeatures_ = flagValue;
@@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(41, "enableVirtualViewRenderState");
markFlagAsAccessed(42, "enableVirtualViewRenderState");
flagValue = currentProvider_->enableVirtualViewRenderState();
enableVirtualViewRenderState_ = flagValue;
@@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(42, "enableVirtualViewWindowFocusDetection");
markFlagAsAccessed(43, "enableVirtualViewWindowFocusDetection");
flagValue = currentProvider_->enableVirtualViewWindowFocusDetection();
enableVirtualViewWindowFocusDetection_ = flagValue;
@@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(43, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(44, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(44, "fuseboxEnabledRelease");
markFlagAsAccessed(45, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(45, "fuseboxNetworkInspectionEnabled");
markFlagAsAccessed(46, "fuseboxNetworkInspectionEnabled");
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
fuseboxNetworkInspectionEnabled_ = flagValue;
@@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(46, "hideOffscreenVirtualViewsOnIOS");
markFlagAsAccessed(47, "hideOffscreenVirtualViewsOnIOS");
flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS();
hideOffscreenVirtualViewsOnIOS_ = flagValue;
@@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(47, "perfMonitorV2Enabled");
markFlagAsAccessed(48, "perfMonitorV2Enabled");
flagValue = currentProvider_->perfMonitorV2Enabled();
perfMonitorV2Enabled_ = flagValue;
@@ -902,7 +920,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(48, "preparedTextCacheSize");
markFlagAsAccessed(49, "preparedTextCacheSize");
flagValue = currentProvider_->preparedTextCacheSize();
preparedTextCacheSize_ = flagValue;
@@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(49, "preventShadowTreeCommitExhaustion");
markFlagAsAccessed(50, "preventShadowTreeCommitExhaustion");
flagValue = currentProvider_->preventShadowTreeCommitExhaustion();
preventShadowTreeCommitExhaustion_ = flagValue;
@@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(50, "releaseImageDataWhenConsumed");
markFlagAsAccessed(51, "releaseImageDataWhenConsumed");
flagValue = currentProvider_->releaseImageDataWhenConsumed();
releaseImageDataWhenConsumed_ = flagValue;
@@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(51, "shouldPressibilityUseW3CPointerEventsForHover");
markFlagAsAccessed(52, "shouldPressibilityUseW3CPointerEventsForHover");
flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover();
shouldPressibilityUseW3CPointerEventsForHover_ = flagValue;
@@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(52, "skipActivityIdentityAssertionOnHostPause");
markFlagAsAccessed(53, "skipActivityIdentityAssertionOnHostPause");
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
skipActivityIdentityAssertionOnHostPause_ = flagValue;
@@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::sweepActiveTouchOnChildNativeGesturesAndro
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "sweepActiveTouchOnChildNativeGesturesAndroid");
markFlagAsAccessed(54, "sweepActiveTouchOnChildNativeGesturesAndroid");
flagValue = currentProvider_->sweepActiveTouchOnChildNativeGesturesAndroid();
sweepActiveTouchOnChildNativeGesturesAndroid_ = flagValue;
@@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(54, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(55, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit(
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(55, "updateRuntimeShadowNodeReferencesOnCommit");
markFlagAsAccessed(56, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(56, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(57, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(57, "useFabricInterop");
markFlagAsAccessed(58, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(58, "useNativeEqualsInNativeReadableArrayAndroid");
markFlagAsAccessed(59, "useNativeEqualsInNativeReadableArrayAndroid");
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
@@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(59, "useNativeTransformHelperAndroid");
markFlagAsAccessed(60, "useNativeTransformHelperAndroid");
flagValue = currentProvider_->useNativeTransformHelperAndroid();
useNativeTransformHelperAndroid_ = flagValue;
@@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(60, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(61, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(61, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(62, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(62, "useRawPropsJsiValue");
markFlagAsAccessed(63, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(63, "useShadowNodeStateOnClone");
markFlagAsAccessed(64, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(64, "useTurboModuleInterop");
markFlagAsAccessed(65, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(65, "useTurboModules");
markFlagAsAccessed(66, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1226,7 +1244,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(66, "virtualViewPrerenderRatio");
markFlagAsAccessed(67, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e7ebd228905c959f5472fba72bc3f99c>>
* @generated SignedSource<<ce6cfc25358a636fcd9cd6605e9008ed>>
*/
/**
@@ -70,6 +70,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableResourceTimingAPI();
bool enableViewCulling();
bool enableViewRecycling();
bool enableViewRecyclingForScrollView();
bool enableViewRecyclingForText();
bool enableViewRecyclingForView();
bool enableVirtualViewDebugFeatures();
@@ -110,7 +111,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 67> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 68> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> cdpInteractionMetricsEnabled_;
@@ -150,6 +151,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableResourceTimingAPI_;
std::atomic<std::optional<bool>> enableViewCulling_;
std::atomic<std::optional<bool>> enableViewRecycling_;
std::atomic<std::optional<bool>> enableViewRecyclingForScrollView_;
std::atomic<std::optional<bool>> enableViewRecyclingForText_;
std::atomic<std::optional<bool>> enableViewRecyclingForView_;
std::atomic<std::optional<bool>> enableVirtualViewDebugFeatures_;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<54a695ed4e9c536cd8650498b9dab2fc>>
* @generated SignedSource<<5c12d5107de20283dd7a2a899685cfa5>>
*/
/**
@@ -179,6 +179,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableViewRecyclingForScrollView() override {
return true;
}
bool enableViewRecyclingForText() override {
return true;
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<60b81844a9af1862b27ed6b4d9be5f59>>
* @generated SignedSource<<6030ed59832efdf42e23c13b689da64f>>
*/
/**
@@ -387,6 +387,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableViewRecycling();
}
bool enableViewRecyclingForScrollView() override {
auto value = values_["enableViewRecyclingForScrollView"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableViewRecyclingForScrollView();
}
bool enableViewRecyclingForText() override {
auto value = values_["enableViewRecyclingForText"];
if (!value.isNull()) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<56fe64198dddcba67062ef52a180128e>>
* @generated SignedSource<<95940b74b0055760be69c501a97a6f45>>
*/
/**
@@ -63,6 +63,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableResourceTimingAPI() = 0;
virtual bool enableViewCulling() = 0;
virtual bool enableViewRecycling() = 0;
virtual bool enableViewRecyclingForScrollView() = 0;
virtual bool enableViewRecyclingForText() = 0;
virtual bool enableViewRecyclingForView() = 0;
virtual bool enableVirtualViewDebugFeatures() = 0;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<3c321b78f9d710e5f3dc3a73e56be298>>
* @generated SignedSource<<998bdf037d58ca747c22ada099d236f0>>
*/
/**
@@ -234,6 +234,11 @@ bool NativeReactNativeFeatureFlags::enableViewRecycling(
return ReactNativeFeatureFlags::enableViewRecycling();
}
bool NativeReactNativeFeatureFlags::enableViewRecyclingForScrollView(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableViewRecyclingForScrollView();
}
bool NativeReactNativeFeatureFlags::enableViewRecyclingForText(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableViewRecyclingForText();
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<15625838a3b9e9ecf3e77940a5f019a6>>
* @generated SignedSource<<89c5d22cd228da57d82843f7e660f262>>
*/
/**
@@ -112,6 +112,8 @@ class NativeReactNativeFeatureFlags
bool enableViewRecycling(jsi::Runtime& runtime);
bool enableViewRecyclingForScrollView(jsi::Runtime& runtime);
bool enableViewRecyclingForText(jsi::Runtime& runtime);
bool enableViewRecyclingForView(jsi::Runtime& runtime);
@@ -451,6 +451,17 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
enableViewRecyclingForScrollView: {
defaultValue: true,
metadata: {
dateAdded: '2025-08-20',
description:
'Enables View Recycling for <ScrollView> via ReactViewGroup/ReactViewManager.',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'none',
},
enableViewRecyclingForText: {
defaultValue: true,
metadata: {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<66a3905e1c94e7769418becc3bace939>>
* @generated SignedSource<<825b92d137ed3253e0badd0696605882>>
* @flow strict
* @noformat
*/
@@ -85,6 +85,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
enableResourceTimingAPI: Getter<boolean>,
enableViewCulling: Getter<boolean>,
enableViewRecycling: Getter<boolean>,
enableViewRecyclingForScrollView: Getter<boolean>,
enableViewRecyclingForText: Getter<boolean>,
enableViewRecyclingForView: Getter<boolean>,
enableVirtualViewDebugFeatures: Getter<boolean>,
@@ -332,6 +333,10 @@ export const enableViewCulling: Getter<boolean> = createNativeFlagGetter('enable
* Enables View Recycling. When enabled, individual ViewManagers must still opt-in.
*/
export const enableViewRecycling: Getter<boolean> = createNativeFlagGetter('enableViewRecycling', false);
/**
* Enables View Recycling for <ScrollView> via ReactViewGroup/ReactViewManager.
*/
export const enableViewRecyclingForScrollView: Getter<boolean> = createNativeFlagGetter('enableViewRecyclingForScrollView', true);
/**
* Enables View Recycling for <Text> via ReactTextView/ReactTextViewManager.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<604963e3616f21f84b65b0291fecb182>>
* @generated SignedSource<<2b0679c42d8e1cb021612807d94d9f4c>>
* @flow strict
* @noformat
*/
@@ -63,6 +63,7 @@ export interface Spec extends TurboModule {
+enableResourceTimingAPI?: () => boolean;
+enableViewCulling?: () => boolean;
+enableViewRecycling?: () => boolean;
+enableViewRecyclingForScrollView?: () => boolean;
+enableViewRecyclingForText?: () => boolean;
+enableViewRecyclingForView?: () => boolean;
+enableVirtualViewDebugFeatures?: () => boolean;