Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console for FlatList/SectionList with scrollEnabled={false} #31697

Open
JeremyBradshaw7 opened this issue Jun 10, 2021 · 25 comments
Labels
Needs: Triage 🔍 Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@JeremyBradshaw7
Copy link

JeremyBradshaw7 commented Jun 10, 2021

Description

We use FlatList and SectionList in various sub-components which we embed into a master component in two ways:

  1. the components are in tabs of a tab control so each sub-component scrolls the FlatList or SectionList so has the benefit of using virtualization - we ensure scrollEnabled={true} in this case,
  2. the components are rendered one under the other in a single ScrollView - we ensure scrollEnabled={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}.

@JeremyBradshaw7 JeremyBradshaw7 changed the title "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console when scrollEnabled={false} Jun 10, 2021
@JeremyBradshaw7 JeremyBradshaw7 changed the title "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console when scrollEnabled={false} "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console for FlarList/SectionList with scrollEnabled={false} Jun 10, 2021
@JeremyBradshaw7 JeremyBradshaw7 changed the title "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console for FlarList/SectionList with scrollEnabled={false} "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation" error in console for FlatList/SectionList with scrollEnabled={false} Jun 10, 2021
@vuletuan
Copy link

Have you fixed it? I got it too. Everything worked well, but it shows errors in the console.

@JeremyBradshaw7
Copy link
Author

JeremyBradshaw7 commented Jun 14, 2021

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.

@KarthikeyanM28
Copy link

KarthikeyanM28 commented Sep 15, 2021

@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?

@CostachescuCristinel
Copy link

CostachescuCristinel commented Sep 15, 2021

@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 TextInputs not 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 ScrollView for randomly sized and arranged components
    • use FlatList for homogeneous components, but without visual grouping of sort
    • use SectionList for homogeneous components organized by groups
    • use VirtualizedList for 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 ListHeaderComponent and ListFooterComponent 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.
    • 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
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).

@Zakyyy
Copy link

Zakyyy commented Oct 24, 2021

I am receiving the same error

@tunm1228
Copy link

tunm1228 commented Nov 2, 2021

1 same issue

@edritech93
Copy link

same issue for me

@fkeloks
Copy link

fkeloks commented Jan 14, 2022

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.

@dhairyasenjaliya
Copy link

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

@NastuzziSamy
Copy link

I personnaly changed ScrollView to a FlatList, which brought me to the same result:

<FlatList
	data={[{}]}
	keyExtractor={() => null}
	renderItem={() => <>{children}</>}
/>

@ASKNJ
Copy link

ASKNJ commented Feb 20, 2022

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.

@vuletuanbt
Copy link

Can you give us the sample code?

@mdrajonhossain
Copy link

@anassbenayed
Copy link

anassbenayed commented May 11, 2022

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.

@GuoguoDad
Copy link

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>
      );
    }

@ZRunner
Copy link

ZRunner commented Aug 3, 2022

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 👀

@GuoguoDad
Copy link

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

facebook-github-bot pushed a commit that referenced this issue Sep 8, 2022
Summary:
Nested `VirtualizedList` is now an [error](646605b), instead of a warning. Issues [here](#31697) and [here](#33024) outline the concern.

## Changelog

[General] [Added] - Added a check to if `scrollEnabled` is not false, if so then fire the `VirtualizedList` error

Pull Request resolved: #34560

Test Plan: Passes all provided automatic tests. In a personal app, there is a situation of nested ScrollViews that had triggered the error. After defining `scrollEnabled={false}` and adding the check, the error no longer appears.

Reviewed By: yungsters

Differential Revision: D39283866

Pulled By: NickGerleman

fbshipit-source-id: 16ae6bbe6bb8b01a54ae18f9e6abf75d11c21c29
@sgolban
Copy link

sgolban commented Nov 25, 2022

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

@jensneuber
Copy link

jensneuber commented Jan 19, 2023

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

@tsheaff
Copy link

tsheaff commented Feb 2, 2023

Yes the fix is here: 62f83a9

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

@tsheaff
Copy link

tsheaff commented Feb 2, 2023

Screenshot 2023-02-02 at 13 39 28

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.

@dibenduCfcs
Copy link

Solution:
do(scolled Enabled = false) child component

OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this issue May 22, 2023
Summary:
Nested `VirtualizedList` is now an [error](facebook@646605b), instead of a warning. Issues [here](facebook#31697) and [here](facebook#33024) outline the concern.

## Changelog

[General] [Added] - Added a check to if `scrollEnabled` is not false, if so then fire the `VirtualizedList` error

Pull Request resolved: facebook#34560

Test Plan: Passes all provided automatic tests. In a personal app, there is a situation of nested ScrollViews that had triggered the error. After defining `scrollEnabled={false}` and adding the check, the error no longer appears.

Reviewed By: yungsters

Differential Revision: D39283866

Pulled By: NickGerleman

fbshipit-source-id: 16ae6bbe6bb8b01a54ae18f9e6abf75d11c21c29
@Mahdi-Farahani
Copy link

🎖 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>

@butri71
Copy link

butri71 commented Oct 7, 2023

Screenshot 2023-02-02 at 13 39 28

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!

@react-native-bot
Copy link
Collaborator

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.

@react-native-bot react-native-bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Sep 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Triage 🔍 Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests