Skip to content

Commit 9dda7ca

Browse files
Abbondanzofacebook-github-bot
authored andcommitted
Generate nested scroll view (#55239)
Summary: This diff adds the ability to experiment with using `NestedScrollView` instead of `ScrollView` as the parent class for `ReactScrollView` on Android. Since Java doesn't support multiple inheritance or conditional parent class selection, this is implemented using code generation: - A Python script (`generate-nested-scroll-view.py`) generates `ReactNestedScrollView.java` and `ReactNestedScrollViewManager.kt` from their respective source files - The generated files are identical to the originals except they extend `NestedScrollView` instead of `ScrollView` - A Buck genrule verifies the generated files stay in sync with source files at build time - The `useNestedScrollViewAndroid` feature flag controls which implementation is used at runtime This approach allows us to safely A/B test the NestedScrollView implementation without requiring JS changes, since both managers register with the same `REACT_CLASS` name (`"RCTScrollView"`). Changelog: [Internal] Differential Revision: D90902079
1 parent c1f5445 commit 9dda7ca

24 files changed

+2521
-28
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<27dfa9832869aa05987fa7cb9740f7ba>>
7+
* @generated SignedSource<<1d13409e4db7a5a48e5d2caf59a5fbcf>>
88
*/
99

1010
/**
@@ -456,6 +456,12 @@ public object ReactNativeFeatureFlags {
456456
@JvmStatic
457457
public fun useNativeViewConfigsInBridgelessMode(): Boolean = accessor.useNativeViewConfigsInBridgelessMode()
458458

459+
/**
460+
* When enabled, ReactScrollView will extend NestedScrollView instead of ScrollView on Android for improved nested scrolling support.
461+
*/
462+
@JvmStatic
463+
public fun useNestedScrollViewAndroid(): Boolean = accessor.useNestedScrollViewAndroid()
464+
459465
/**
460466
* Use shared animation backend in C++ Animated
461467
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<6fb19235ba5049e07952cfa1b564e9e2>>
7+
* @generated SignedSource<<58a693fe003a9a19311fc0134d071154>>
88
*/
99

1010
/**
@@ -91,6 +91,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
9191
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
9292
private var useFabricInteropCache: Boolean? = null
9393
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
94+
private var useNestedScrollViewAndroidCache: Boolean? = null
9495
private var useSharedAnimatedBackendCache: Boolean? = null
9596
private var useTraitHiddenOnAndroidCache: Boolean? = null
9697
private var useTurboModuleInteropCache: Boolean? = null
@@ -737,6 +738,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
737738
return cached
738739
}
739740

741+
override fun useNestedScrollViewAndroid(): Boolean {
742+
var cached = useNestedScrollViewAndroidCache
743+
if (cached == null) {
744+
cached = ReactNativeFeatureFlagsCxxInterop.useNestedScrollViewAndroid()
745+
useNestedScrollViewAndroidCache = cached
746+
}
747+
return cached
748+
}
749+
740750
override fun useSharedAnimatedBackend(): Boolean {
741751
var cached = useSharedAnimatedBackendCache
742752
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<6410500cbf5a05b1f34efd12d1e83bdf>>
7+
* @generated SignedSource<<698641fe5c1a9f4933321d5b155467d1>>
88
*/
99

1010
/**
@@ -170,6 +170,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
170170

171171
@DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): Boolean
172172

173+
@DoNotStrip @JvmStatic public external fun useNestedScrollViewAndroid(): Boolean
174+
173175
@DoNotStrip @JvmStatic public external fun useSharedAnimatedBackend(): Boolean
174176

175177
@DoNotStrip @JvmStatic public external fun useTraitHiddenOnAndroid(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<6c664176cf41ffb9f2bf820f15ed7463>>
7+
* @generated SignedSource<<6ab1616102fb0807ad936f4333cb44c8>>
88
*/
99

1010
/**
@@ -165,6 +165,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
165165

166166
override fun useNativeViewConfigsInBridgelessMode(): Boolean = false
167167

168+
override fun useNestedScrollViewAndroid(): Boolean = false
169+
168170
override fun useSharedAnimatedBackend(): Boolean = false
169171

170172
override fun useTraitHiddenOnAndroid(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<1403b30f4dc3d18f39303d91d6824bda>>
7+
* @generated SignedSource<<f83c3d5107d900273c09061d6822f83d>>
88
*/
99

1010
/**
@@ -95,6 +95,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
9595
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
9696
private var useFabricInteropCache: Boolean? = null
9797
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
98+
private var useNestedScrollViewAndroidCache: Boolean? = null
9899
private var useSharedAnimatedBackendCache: Boolean? = null
99100
private var useTraitHiddenOnAndroidCache: Boolean? = null
100101
private var useTurboModuleInteropCache: Boolean? = null
@@ -812,6 +813,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
812813
return cached
813814
}
814815

816+
override fun useNestedScrollViewAndroid(): Boolean {
817+
var cached = useNestedScrollViewAndroidCache
818+
if (cached == null) {
819+
cached = currentProvider.useNestedScrollViewAndroid()
820+
accessedFeatureFlags.add("useNestedScrollViewAndroid")
821+
useNestedScrollViewAndroidCache = cached
822+
}
823+
return cached
824+
}
825+
815826
override fun useSharedAnimatedBackend(): Boolean {
816827
var cached = useSharedAnimatedBackendCache
817828
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<ad478388cb9a64690c71fe8fa06c1c79>>
7+
* @generated SignedSource<<c2e44ee4f139a99b8179f750b416ed2f>>
88
*/
99

1010
/**
@@ -165,6 +165,8 @@ public interface ReactNativeFeatureFlagsProvider {
165165

166166
@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean
167167

168+
@DoNotStrip public fun useNestedScrollViewAndroid(): Boolean
169+
168170
@DoNotStrip public fun useSharedAnimatedBackend(): Boolean
169171

170172
@DoNotStrip public fun useTraitHiddenOnAndroid(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import com.facebook.react.views.progressbar.ReactProgressBarViewManager
5252
import com.facebook.react.views.safeareaview.ReactSafeAreaViewManager
5353
import com.facebook.react.views.scroll.ReactHorizontalScrollContainerViewManager
5454
import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager
55+
import com.facebook.react.views.scroll.ReactNestedScrollViewManager
5556
import com.facebook.react.views.scroll.ReactScrollViewManager
5657
import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager
5758
import com.facebook.react.views.switchview.ReactSwitchManager
@@ -139,7 +140,8 @@ constructor(private val config: MainPackageConfig? = null) :
139140
ReactHorizontalScrollViewManager(),
140141
ReactHorizontalScrollContainerViewManager(),
141142
ReactProgressBarViewManager(),
142-
ReactScrollViewManager(),
143+
if (ReactNativeFeatureFlags.useNestedScrollViewAndroid()) ReactNestedScrollViewManager()
144+
else ReactScrollViewManager(),
143145
ReactSwitchManager(),
144146
ReactSafeAreaViewManager(),
145147
SwipeRefreshLayoutManager(),
@@ -173,7 +175,11 @@ constructor(private val config: MainPackageConfig? = null) :
173175
ReactSafeAreaViewManager.REACT_CLASS to
174176
ModuleSpec.viewManagerSpec { ReactSafeAreaViewManager() },
175177
ReactScrollViewManager.REACT_CLASS to
176-
ModuleSpec.viewManagerSpec { ReactScrollViewManager() },
178+
ModuleSpec.viewManagerSpec {
179+
if (ReactNativeFeatureFlags.useNestedScrollViewAndroid())
180+
ReactNestedScrollViewManager()
181+
else ReactScrollViewManager()
182+
},
177183
ReactSwitchManager.REACT_CLASS to ModuleSpec.viewManagerSpec { ReactSwitchManager() },
178184
SwipeRefreshLayoutManager.REACT_CLASS to
179185
ModuleSpec.viewManagerSpec { SwipeRefreshLayoutManager() },

0 commit comments

Comments
 (0)