"VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console for FlatList/SectionList with scrollEnabled={false}
Description
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
FlatListorSectionListso 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(R) Core(TM) 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}.
Have you fixed it? I got it too. Everything worked well, but it shows errors in the console.
I built a wrapper component to un-virtualize the list when scrollEnabled={false} to get around this, eg:
import React from 'react';
import { FlatListProps, FlatList as NativeFlatList, View } from 'react-native';
// Custom FlatList that doesnt try to virtualize when scrollEnabled={false}
// Do this to avoid "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console
export default function FlatListWrapper<ItemT>(props: FlatListProps<ItemT>) {
if (props.scrollEnabled === false) {
return (
<View style={props.style}>
{!!props.ListHeaderComponent && !!props.data && props.data.length > 0 && props.ListHeaderComponent}
{!!props.ListEmptyComponent && (!props.data || props.data.length === 0) && props.ListEmptyComponent}
{!!props.data && props.data.map((item: any, index) => <View key={!!props.keyExtractor ? props.keyExtractor(item, index) : `${index}`}>
{!!props.renderItem && props.renderItem({
item, index, separators: {
highlight: () => null,
unhighlight: () => null,
updateProps: (select: 'leading' | 'trailing', newProps: any) => null
}
})}
{!!props.ItemSeparatorComponent && !!props.data && index < props.data.length - 1 && typeof props.ItemSeparatorComponent === 'function' && (props.ItemSeparatorComponent as any)()}
</View>)}
{!!props.ListFooterComponent && !!props.data && props.data.length > 0 && props.ListFooterComponent}
</View>
);
}
return <NativeFlatList {...props} />;
}
And for SectionList:
import React from 'react';
import { DefaultSectionT, SectionList as NativeSectionList, SectionListProps, View } from 'react-native';
// Custom SectionList that doesnt try to virtualize when scrollEnabled={false}
// Do this to avoid "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console
export default function SectionListWrapper<ItemT = any, SectionT = DefaultSectionT>(props: SectionListProps<ItemT, SectionT>) {
if (props.scrollEnabled === false) {
return (
<View style={props.style}>
{!!props.ListHeaderComponent && !!props.sections && props.sections.length > 0 && props.ListHeaderComponent}
{!!props.ListEmptyComponent && (!props.sections || props.sections.length === 0) && props.ListEmptyComponent}
{!!props.sections && props.sections.map((item: any, index) => <View key={!!props.keyExtractor ? props.keyExtractor(item, index) : `${index}`}>
{!!props.renderSectionHeader && props.renderSectionHeader({ section: item })}
{!!props.SectionSeparatorComponent && !!props.sections && typeof props.SectionSeparatorComponent === 'function' && (props.SectionSeparatorComponent as any)()}
{item.data.map((subitem: any, subindex) => <View key={`${subindex}`}>
{!!props.renderItem && props.renderItem({
item: subitem, section: item, index: subindex, separators: {
highlight: () => null,
unhighlight: () => null,
updateProps: (select: 'leading' | 'trailing', newProps: any) => null
}
})}
{!!props.ItemSeparatorComponent && !!item.data && subindex < item.data.length - 1 && typeof props.ItemSeparatorComponent === 'function' && (props.ItemSeparatorComponent as any)()}
</View>)}
{!!props.SectionSeparatorComponent && !!props.sections && typeof props.SectionSeparatorComponent === 'function' && (props.SectionSeparatorComponent as any)()}
{!!props.renderSectionFooter && props.renderSectionFooter({ section: item })}
</View>)}
{!!props.ListFooterComponent && !!props.sections && props.sections.length > 0 && props.ListFooterComponent}
</View>
);
}
return <NativeSectionList {...props} />;
}
(this is TypeScript - I'm not sure about the separators implementation but this seems to circumvent the issue)
I'd still rather have the framework take care of this for me.
@beingArkReal raised issue tagged under question #28080, @CostachescuCristinel #27945 both issue are closed by the bot without any solution????
There is a question in StackOverflow https://stackoverflow.com/questions/67623952/error-virtualizedlists-should-never-be-nested-inside-plain-scrollviews-with-th
all of these not giving any clarity about this Error.
My case is
<Animated.ScrollView
style={{ flex: 1 }}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
{ listener: (e) => handleScroll(e), useNativeDriver: false }
)}
nestedScrollEnabled={true}
>
<Report />
<ShortNav />
<RequestedTripList setLoadIndent={setLoadIndent} /> // FlatList
</Animated.ScrollView>
How to handle this case?
@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 as TouchableXXX) can also fight for the touch events (stack overflow has a number of posts on how to fix a ScrollView stealing touches from TouchableXXX childs).
Due to this flaw in the scrollable component, many issues arise:
- the already mentioned scrollables stealing touches from touchables
- nested
TextInputsnot being scrollable themselves - multiple scrollables scrolling at the same time and in the same direction (this one, I must say, it's interesting for a parallax effect, though, it is definitely an undesired side-effect).
There 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.- As the warning says, do not nest scrollables with the same orientation (and, I would add, do not do this deeper that 1 parent and 1 child). Try to stay as much as possible within the RN limitations, and use the available scrollable components and their props to your advantage:
- use
ScrollViewfor randomly sized and arranged components - use
FlatListfor homogeneous components, but without visual grouping of sort - use
SectionListfor homogeneous components organized by groups - use
VirtualizedListfor a combination of ScrollView/FlatList/SectionList -like behavior, but where you actually need a lot of control over the item rendering and size calculation - Use
ListHeaderComponentandListFooterComponentto 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. - Disable scroll when the content size is smaller than the container size, to prevent scroll events from interfering with each other. This can possibly improve the user experience as the scroll will stay fixed as much as possible
- use
class AutoDisableScrollable extends React.PureComponent {
state = { container: 0, content: 0 };
onLayout = (layoutEvent) => {
const { nativeEvent: { layout: { width, height } } } = layoutEvent;
this.setSize("container", width, height, () => {
if (typeof (this.props.onLayout) === "function") {
this.props.onLayout(layoutEvent);
}
});
}
onContentSizeChange = (width, height) => {
this.setSize("content", width, height, () => {
if (typeof (this.props.onLayout) === "function") {
this.props.onContentSizeChange(width, height);
}
});
}
setSize = (ofItem, width, height, callback) => {
const currentSize = this.state[ofItem];
const newSize = (this.props.horizontal !== true) ? height : width;
if (newSize !== currentSize) {
this.setState({ [ofItem]: newSize }, callback);
} else {
callback();
}
}
render = () => {
const {
__ref, // Use this instead of the "ref" prop
ScrollableComponent, // ScrollView, FlatList, SectionList, VirtualizedList
scrollEnabled, bounces, onLayout, onContentSizeChange, children, ...props
} = this.props;
const { container, content } = this.state;
const __scrollEnabled = ((scrollEnabled !== false) && (content > container));
const __bounces = ((bounces !== false) && (__scrollEnabled === true));
return <ScrollableComponent
ref={__ref}
scrollEnabled={__scrollEnabled}
bounces={__bounces}
onLayout={this.onLayout}
onContentSizeChange={this.onContentSizeChange}
{...props}
>
{children}
</ScrollableComponent>;
}
}
<AutoDisableScrollable
ScrollableComponent={ScrollView}
style={{ flex: 1 }}
...
>
...
</AutoDisableScrollable>
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.Views and a PanResponder 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 the translateX / translateY props of a child content container Animated.View to "scroll" the items (the approach is similar to how scrollables in RN are built).
I am receiving the same error
1 same issue
same issue for me
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.
any solution this error occurs only after 0.61 to 0.64 upgrade till it was fine now I cant change to each and every pages
I personnaly changed ScrollView to a FlatList, which brought me to the same result:
<FlatList
data={[{}]}
keyExtractor={() => null}
renderItem={() => <>{children}</>}
/>
I have found a solution for this. You can wrap your Component inside a frame and pass it to renderItem in your FlatList. Then you can use as many FlatList inside FlatList but eliminate the use of ScrollView , if you don't wnat to see this warning again.
Can you give us the sample code?
<FlatList data={data} renderItem={renderItem} />
Same error here, using a scrollview as parent view, and nesting a SelectBox from react-native-multi-selectbox package. Any update on this?
Update 1: I was able to solve this by adding listOptionProps={{nestedScrollEnabled: true}} like this:
<ScrollView>
<SelectBox
label="Select single"
options={serverData}
listOptionProps={{nestedScrollEnabled: true}}
value={input.elementSelected}
onChange={event =>
inputHandlerLang('elementSelected', event, key)
}
hideInputFilter={false}
/>
</ScrollView>
the error still present but scrolling within SelectBox works as well as within the parent scrollview. I also do have to suppress the error with LogBox. I don't know if there are any drawbacks to this but I'll try to test this more.
Directly find the source code and comment out the console error
path: react-native/Libraries/Lists/VirtualizedList.js line: 1135
if (__DEV__) {
ret = (
<ScrollView.Context.Consumer>
{scrollContext => {
if (
scrollContext != null &&
!scrollContext.horizontal ===
!horizontalOrDefault(this.props.horizontal) &&
!this._hasWarned.nesting &&
this.context == null
) {
// TODO (T46547044): use React.warn once 16.9 is sync'd: https://github.com/facebook/react/pull/15170
// console.error(
// 'VirtualizedLists should never be nested inside plain ScrollViews with the same ' +
// 'orientation because it can break windowing and other functionality - use another ' +
// 'VirtualizedList-backed container instead.',
// );
this._hasWarned.nesting = true;
}
return innerRet;
}}
</ScrollView.Context.Consumer>
);
}
Any update on this issue?
Inserting one or multiple FlatLists inside a ScrollView, even with scrolling inside the lists being disabled, sounds like a common thing to me. It avoids having to .map() our data lists ourselves and brings a few other benefits from the FlatList component. And I don't think "remove the error from the source code yourself" is a correct solution either 👀
Any update on this issue? Inserting one or multiple FlatLists inside a ScrollView, even with scrolling inside the lists being disabled, sounds like a common thing to me. It avoids having to
.map()our data lists ourselves and brings a few other benefits from the FlatList component. And I don't think "remove the error from the source code yourself" is a correct solution either 👀
Before version 0.63, it was only a warning, and then it became an error. If you have a better way, I am not willing to change the source code
A solution for me was to create a custom FlatList component, something like this
import React from 'react'
import { FlatList, FlatListProps } from 'react-native'
import { uniqueId } from 'lodash'
/** This component was created to avoid the Warning about nested virtualized lists when nesting a flatlist inside a scrollview */
const FlatListScrollView = <T extends any>({ children, ...props }: React.PropsWithChildren<Partial<FlatListProps<T>>>) => {
return (
<FlatList
data={[undefined] as any}
showsVerticalScrollIndicator={false}
renderItem={() => <React.Fragment>{children}</React.Fragment>}
keyExtractor={(_, index) => uniqueId('flat-list-scroll-view-element-' + index)}
initialNumToRender={1}
maxToRenderPerBatch={1}
{...props}
/>
)
}
export default FlatListScrollView
As far as I see it this is fixed in RN 0.71 Changelog
Plain view of the changelog with line reference: https://github.com/facebook/react-native/blob/main/CHANGELOG.md?plain=1#L63
Yes the fix is here: https://github.com/facebook/react-native/commit/62f83a9fad027ef0ed808f7e34973bb01cdf10e9
One-liner to just supress the error when scrollEnabled is false. Which is sensible. I think this also means we're safe to ignore this error if we know scrollEnabled is false, even before we upgrade to RN 0.71
thanks for the fix @annepham25

here's a more detailed view of which release tags this commit is included in
I'm using Expo which doesn't support 0.71 yet, so I can't get on the latest yet.
Solution: do(scolled Enabled = false) child component
🎖 Simple Solution To Fix It: 100% works 🤩
1- Add nestedScrollEnabled={true} to ScrollView 2- Add scrollEnabled={false} to FlatList
<ScrollView nestedScrollEnabled={true} >
<FlatList
scrollEnabled={false}
...
/>
</ScrollView>
here's a more detailed view of which release tags this commit is included in
I'm using Expo which doesn't support 0.71 yet, so I can't get on the latest yet.
I'm on Expo SDK48 which support RN 0.71.13 and error is still shown. I assume the fix to be included in SDK49 but not go there yet!
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
This issue was closed because it has been stalled for 7 days with no activity.
How is adding scrollEnabled={false} to a nested FlatList a solution? Don't we want to use nested flat lists, as they are, for things like select boxes?
Furthermore, I've seen sooo many people recommend scrollEnabled={false} on a FlatList, then promote wrapping it in a ScrollView which I thought was bad practice, especially when you want to start listening to scroll events, or add RefreshControl into the mix? You want to do those things directly on the FlatList.
The only solution I can see, so far, (which also seems strange) is rendering all of an app's main content in a FlatList as one item, seeing as they are virtualised and can be nested within each other safely.
Or a new prop for ScrollView that allows for nested virtualized lists. It's up to the developer to use responsibly. ScrollView is super handy for humble pages (like a settings screen) that need to overflow a little on the y-axis, and there's typically not much content in them anyway.
We need a legit solution here.