facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
117.23k stars 24.1k forks source link

[IOS] FlatList inside FlatList blocks other touchables (children of parent FlatList) in IOS during scroll #35332

Open PratthamArora opened 1 year ago

PratthamArora commented 1 year ago

Description

inside a parent vertical FlatList, use a horizontal child FlatList. While scrolling the child FlatList the other children of parent FlatList cannot trigger onPress. They are blocked.

Version

0.66.0, 0.69.0

Output of npx react-native info

System: OS: macOS 12.1 CPU: (8) x64 Apple M1 Pro Memory: 28.88 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 14.10.0 - ~/.nvm/versions/node/v14.10.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v14.10.0/bin/yarn npm: 6.14.8 - ~/.nvm/versions/node/v14.10.0/bin/npm Watchman: Not Found Managers: CocoaPods: 1.11.0 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: iOS 15.0, DriverKit 20.4, macOS 11.3, tvOS 15.0, watchOS 8.0 Android SDK: Not Found IDEs: Android Studio: 2021.2 AI-212.5712.43.2112.8815526 Xcode: 13.0/13A233 - /usr/bin/xcodebuild Languages: Java: 14.0.2 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 18.0.0 => 18.0.0 react-native: 0.69.6 => 0.69.6 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

inside a parent vertical flatlist, use a horizontal child flatlist. While scrolling the child flatlist the other children of parent flatlist cannot trigger onPress. They are blocked.

Snack, code example, screenshot, or link to a repository

` import React, { Component } from 'react'; import { View, Text, TouchableOpacity, SafeAreaView, FlatList, ScrollView } from 'react-native';

const App = ()=> { const renderListItem = ({ item, index }: any) => { return (

{ console.log(" onScroll called in child Flatlist "); } } /> console.log('on Press')}> Count
    );
};

return (
  <SafeAreaView>
      <FlatList
        data={['']}
        renderItem={renderListItem}
        onScroll={() => {
          console.log(" onScroll called Flatlist ");
        }
      }
      />
      </SafeAreaView>

);

} export default App

PratthamArora commented 1 year ago

Detailed RCA -

Whenever a component is clicked while the scroll is active the onPress function of the component is not being called, however components that were wrapped with touchable opacity components are highlighted i.e visible visual feedback, basically, the touch is being registered but has not been propagated to the component. The onPress has its own life cycle. The Pressability class relies on various variables such as the current state, the previous state, and the current signal being propagated to this class to complete the entire life cycle of this method. For onPress to be triggered, the signal that should be received is RESPONDER_RELEASE whereas in our case the signal that is received is RESPONDER_TERMINATED, which means that this event should be canceled.

The gesture recognizer system in React Native works on the Responder System wherein for a particular view to register and execute certain gestures such as a click or a swipe, the current view needs to have a lock on it i.e it needs to be the current responder for that entire page. The way the Responder System works is that it is a singleton at the global level and at one particular moment of time only one view can be the responder, hence all the other views and any gesture triggered on that view is canceled if that view is not the current responder.

One of the issues is when a scroll view is used with onScroll method defined. This is a bubbling event that is emitted from the native to the JS and for a view to consume a particular event and take action on it needs to be the responder since this event is fired every 16 ms on iOS, the scroll view is the current responder until the scrolling is stopped which cancels any other gesture triggered on any other component.

As per the entire journey defined in the Responder System, _handleScrollShouldSetResponder method is called again and again to set the scroll view as the current responder which is called in the ResponderEventPlugin from React which then triggers this method and sets the scroll view as the current responder.

Another issue is where a FlatList is nested inside a parent FlatList. It exhibits the same behavior as described above. Since a FlatList natively extends ScrollView only, it inherits the same characteristics.

After debugging the native implementations and working of ScrollView and it’s events on Android and iOS, and the lifecycle of gestures and the Responder System in React Native and React, I found that _handleScrollShouldSetResponder method is never called on Android but is called on IOS every time an OnScroll event is emitted from native to JS. This method is responsible for setting the scroll view as the current responder.

In Android whenever a Scroll Event is emitted, a boolean responderIgnoreScroll set to true is sent as metadata which is consumed in React where the transfer of responder occurs. This flag is missing from the Scroll Event data in IOS. Upon sending this flag with the value set to true, the _handleScrollShouldSetResponder method wasn’t called on IOS just like Android but because of this when I touch a child component of Scrollview and then drag to scroll it does not cancel the touch event and the child view becomes the responder and registers the onPress method instead of the desired behavior where the Scrollview should have scrolled.

PratthamArora commented 1 year ago

Related issue - https://github.com/facebook/react-native/issues/35333

PratthamArora commented 1 year ago

tried to use disableScrollViewPanResponder flag on my parent FlatList which does fix the issue to some level but due to this when I touch a child component of parent FlatList and then drag to scroll it does not cancel the touch event and the child view becomes the responder and registers the onPress method instead of the desired behavior where the parent FlatList should have scrolled.

PratthamArora commented 1 year ago

Old Reported issue - https://github.com/facebook/react-native/issues/10822

Ggayane commented 1 year ago

Does anyone have an update for this kind of issue? I'm facing the same problem, I have a screen with flat list and it was working perfect on RN v.0.64, after updating to v.0.70 it started to work just terrible, the rendering takes much more longer, and there switching screens blocks the touchable, scrolling, etc...

PratthamArora commented 1 year ago

any help on this?

PratthamArora commented 1 year ago

In the sample app, I used a FlatList as the vertical parent list and a FlatList as the child horizontal list. I have also defined onScroll event with scrollEventThrottle flag in ScrollView with a simple toast message to simulate a click action behavior As you can see in the video below, when the horizontal list is scrolling, the click action for other children of the parent list is not triggered. Whereas when the child horizontal list is at a stationary position, the click action works fine.

Link to the video - https://drive.google.com/file/d/1I6FQMkCYzBgSNQO-LR8t2oumgxt2mBFR/view?usp=sharing

github-actions[bot] commented 11 months ago

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.

PratthamArora commented 11 months ago

This is still open

github-actions[bot] commented 11 months ago
:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.
github-actions[bot] commented 5 months ago

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.

scblason commented 5 months ago

I'm having a similar issue, but happens randomly after a few navigations into this view (A scroll view with nested FlatLists).

BitPhinix commented 3 months ago

Experiencing the same issue