"VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console for FlatList/SectionList with scrollEnabled={false}
See original GitHub issueDescription
We use FlatList
and SectionList
in various sub-components which we embed into a master component in two ways:
- the components are in tabs of a tab control so each sub-component scrolls the
FlatList
orSectionList
so has the benefit of using virtualization - we ensurescrollEnabled={true}
in this case, - the components are rendered one under the other in a single
ScrollView
- we ensurescrollEnabled={false}
in this case,
We get the “VirtualizedLists should never be nested inside plain ScrollViews with the same orientation” console error repeatedly in the case of 2. I think when scrollEnabled={false}
it should turn off virtualization and suppress this console error, and render the list internally using standard maps. Yes I can build my own wrapper components to do this, but I think the framework should do it naturally.
React Native version:
System: OS: macOS 11.3.1 CPU: (8) x64 Intel® Core™ i7-4770HQ CPU @ 2.20GHz Memory: 388.60 MB / 16.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 15.1.0 - /usr/local/bin/node Yarn: 1.19.1 - ~/.yarn/bin/yarn npm: 7.0.8 - /usr/local/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.10.1 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4 Android SDK: API Levels: 23, 28, 29, 30 Build Tools: 23.0.1, 23.0.2, 25.0.0, 25.0.1, 25.0.2, 26.0.2, 26.0.3, 27.0.0, 27.0.3, 28.0.2, 28.0.3, 29.0.2, 29.0.3 System Images: android-16 | Google APIs Intel x86 Atom, android-22 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom_64 Android NDK: Not Found IDEs: Android Studio: 4.1 AI-201.8743.12.41.7199119 Xcode: 12.5/12E262 - /usr/bin/xcodebuild Languages: Java: 1.8.0_271 - /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/bin/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.1 => 17.0.1 react-native: 0.64.1 => 0.64.1 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found
Steps To Reproduce
As per the description.
Expected Results
Would expect not to see the console error on a FlatList or SectionList which has scrollEnabled={false}
.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:9
- Comments:18
Top GitHub Comments
@KarthikeyanM28 I have read a number of posts on the web, and kind of found some explanation for this warning. I am typing from what I can remember. I think I have found at some point someone pointing to the native source code of the scrollable component behind the ScrollView and FlatList components and explaining what happens, but I cannot find that post right now…
Before I start, I haven’t found an official RN team answer as to why this warning and what the actual reason is for it.
Anyway, the story was something like this: The scrollable component source code that is behind the RN scrollables has a difficult time identifying whether it should steal and handle the touch events, or to let it pass-through. The problem is that this component was built similar to a singleton pattern, where one, and only one, of such components ever exists in the tree branch it is used in - you can have adjacent branches with this component, but no nested sub-branches.
The difficulty of the scrollable component when nesting multiple instances in sub-branches is that the instances will fight each other as to which one is eligible for stealing the touch events. Usually, this is resolved according to the event bubbling phase order (which flows bottom-up, child-to-parent), but the
zIndex
and the capture phase events (which flow top-down, parent-to-child, and before the bubbling phase) can screw this up. Not to mention that some other components (such asTouchableXXX
) can also fight for the touch events (stack overflow has a number of posts on how to fix a ScrollView stealing touches fromTouchableXXX
childs).Due to this flaw in the scrollable component, many issues arise:
TextInputs
not being scrollable themselvesThere are also many component layout and sizing issues due to this. The nature of the scrollable components is to render dynamically sized content. This makes layout calculations hard for RN since it has to continuously bounce between parents and childs to calculate the layouts, until all resolved dimensions will stabilize.
{ height: "100%" }
(or width for horizontal scrollables) or{ flexGrow: 1 }
can make for a really troubling case where the scrollable content size grows, then child’s size grows as well, causing the content size to grow again, and this process repeating infinitely until the app crashes.The issue is somewhat non-conflicting when limiting nested scrollables to 2 instances and using different scrollable directions. Scrollables have a gesture distance activation threshold (if I remember correctly, there must be a delta of +/-10dp) in the direction observed before the scrollable will deem itself eligible for stealing the touch events. Since the horizontal and vertical directions look for deltas on different axes, they do not interfere with each other that much.
This explains the
... with the same orientation
part of the warning. However, I have had a number of instances where both scrolling directions would activate at the same time, making it difficult for the user to follow the intended touch gesture. Again, nest one more with the same orientation, and you’re back to the issues presented above.As far as I could conclude from my findings on the web, the React Native team solution was… well, this warning.
They did not enforce this rule to completely prevent you from nesting scrollables, at any depth and with whichever orientation you desire, but they are also not supporting use cases of nested scrollables with the same orientation.
The consequence is that reporting a bug for misbehavior in these cases will not be their responsibility - it is not supported. Also, I do not think that they have any plans at this time to write a proper scrollable component that would behave correctly.
It is worth mentioning however that nesting scrollables with a different orientation is a supported use case, as also confirmed by the
nestedScrollEnabled
prop required to enable nested scrolling on Android (now enabled by default). Also, if I am not mistaken, the problem of this scrollable component also had to do with differences in scroll handling and behavior between iOS and Android.So, if you’re wondering about possible solutions:
LogBox.ignoreLogs(['VirtualizedLists should never be nested']);
Workaround. And then you should test a lot between iOS and Android to make sure that the behavior is consistent and does not fall apart.
ScrollView
for randomly sized and arranged componentsFlatList
for homogeneous components, but without visual grouping of sortSectionList
for homogeneous components organized by groupsVirtualizedList
for a combination of ScrollView/FlatList/SectionList -like behavior, but where you actually need a lot of control over the item rendering and size calculationListHeaderComponent
andListFooterComponent
to render dynamic items at the beginning and end of FlatList or SectionList. These components do not have to be the same form or size as the rest of the items rendered in the list.I hope that this wall of text helps you and others to get scrollables working correctly. I do have ideas for a custom scrollable component that uses
Animated.View
s and aPanResponder
to handle touches and “scroll”, however, I have not materialized this yet. In case you want to dig into this, a container View would use a PanResponder to respond to touch events, and in given conditions, would accept handling touches and update thetranslateX
/translateY
props of a child content container Animated.View to “scroll” the items (the approach is similar to how scrollables in RN are built).Hello,
I have the same error, and the proposed solutions do not suit me very well… Is it possible to disable this error when
scrollEnabled={false}
? If the scroll is disabled, I don’t see how it can be a problem to use a FlatList in a ScrollView.