facebook / react-native

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

React Native onEndReached FlatList trigger error #36529

Open Pietro-Putelli opened 1 year ago

Pietro-Putelli commented 1 year ago

Description

I'm developing a react native app using the version 0.71.3 and I'm having some problems with the extremely discussed onEndReached callback.

React Native Version

0.71.3

Output of npx react-native info

System: OS: macOS 12.6.3 CPU: (4) x64 Intel(R) Core(TM) i5-6267U CPU @ 2.90GHz Memory: 228.86 MB / 8.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.14.0 - /usr/local/bin/node Yarn: 1.22.19 - /usr/local/bin/yarn npm: 9.5.0 - /usr/local/bin/npm Watchman: Not Found Managers: CocoaPods: 1.12.0 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1 Android SDK: Not Found IDEs: Android Studio: 2021.3 AI-213.7172.25.2113.9123335 Xcode: 14.2/14C18 - /usr/bin/xcodebuild Languages: Java: 17.0.2 - /usr/bin/javac npmPackages: @react-native-community/cli: Not Found react: 18.2.0 => 18.2.0 react-native: 0.71.3 => 0.71.3 react-native-macos: Not Found npmGlobalPackages: react-native: Not Found

Steps to reproduce

Here's my onEndReached re-implementation to prevent the callback to be triggered even if the list is empty and to trigger it only once:

if (!endReached.current && dataSize > 0) {
  endReached.current = true;
  onEndReached?.();
}

And every time the data size changes, the endReached is set to false again:

useEffect(() => {
  endReached.current = false;
}, [dataSize]);

And then the list:

<FlatList onEndReachedThreshold={0.2} />

Now the problem arise when loading the list for the first time, the onEndReached callback is triggered even if the list is empty and once the list is populated, downloading items asynchronously, and reach the end the callback it's not triggered.

But if the list data area cached, the second time I load the list the onEndReached is not triggered at the beginning but on the right way.

It seems that the error, or I think the bug, arise when the list is empty because data are loading, indeed if I set a condition to render the list only after the data are fully loaded, the onEndReached is trigged correctly.

github-actions[bot] commented 1 year ago
:warning: Newer Version of React Native is Available!
:information_source: You are on a supported minor version, but it looks like there's a newer patch available. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.
Garamani commented 1 year ago

I'm using:

and experiencing the same issue:

As a workaround:

ram95krishh commented 1 year ago

+1 on this issue, onEndReached isn't triggering on android, but works fine on iOS. No matter what threshold I set, it just doesn't trigger the onEndReached callback

slrent commented 10 months ago

+1 on this issue too. It seems onEndReached triggers when the screen gets initially loaded but doesn't trigger when the end of screen is reached

React: 18.2.0 React Native: 0.71.8

wangjh7 commented 10 months ago

+1 on this error.

troberts-28 commented 9 months ago

+1 on this too

ebenezer710 commented 8 months ago
iwakawamarcos commented 8 months ago

Your problem most likely occurs in the onEndReach call when the asynchronous data has not yet been loaded. I recommend using something that prevents the list from being rendered before the data that will make it up arrives. For example: {this.state.data.length > 0 && ( <FlatList...

With that you will solve the problem of the first empty call of onEndReach and it will start working fine

ebenezer710 commented 8 months ago

i solved by adding the { } to the const [data, setData] = useState( [ { } ] ); the Data is what is been rendered on the FlatList.

<FlatList
    ref={flatListRef}
    data={data}
    keyExtractor={(item) => item.IdProduto.toString()}
    renderItem={({ item }) => (
        )}
    onScroll={handleScroll}
    onEndReachedThreshold={0.9}
    onEndReached={() => {
        if (!loading && hasMore) {
            fetchData(subgroup || group || null, page, searchText, geral);
        }
    }}
    refreshControl={<RefreshControl refreshing={refreshing} />}
/>
ebenezer710 commented 8 months ago

Or try using Flash-List from Shopfy it is 1000% faster.

Aryk commented 7 months ago

For those having this issue, here is my solution, I essentially switch the key on the FlatList, causing it to "re-mount" when data is loaded. This is probably not "great" for performance, but once the flatlist has data, nothing should be changing.

interface IUseOnEndReachedFix<Data> {
  data: Data[];
  key?: string;
  onEndReachedThreshold?: number;
  onEndReached?: () => any;
}
const useOnEndReachedFix = <Data, >({
  data,
  key = "default",
  onEndReachedThreshold,
  onEndReached,
}: IUseOnEndReachedFix<Data>) => ({
  // Aryk: I was having an issue where sometimes on the initial render of the FlatList, the data would load but
  // onEndReached would not trigger when you get to the end of the list. Lada reported this on the Messages
  // screen, but I've also noticed other folks having issues with this as well.
  // https://github.com/facebook/react-native/issues/36529#issuecomment-1814359203
  key: data?.length ? key : "onEndReachedFix",
  // When you have an empty array (like the initial state of "items"), it's calling onEndReached. 🤦
  //  https://github.com/facebook/react-native/pull/39574
  onEndReached: data?.length ? onEndReached : null,
  onEndReachedThreshold
});
emanusantos commented 6 months ago

+1

No solutions yet?

dan-pugsley commented 1 week ago

Or try using Flash-List from Shopify it is 1000% faster.

Wow, this solved it for me. General performance increase too and works with Expo. Took 2 mins to replace FlatList.