From 3485e9ed871886b3e7408f90d623da5c018da493 Mon Sep 17 00:00:00 2001 From: YunPeng Chong Date: Mon, 14 Oct 2024 15:09:25 -0700 Subject: [PATCH] Fix onEndReached not being called when getItemLayout is present and we scroll past render window (#46990) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46990 This is a reattempt land D63643856 to fix the scroll onendreached event not firing. Changelog: [General][Fixed] - Fix onEndReached not being called when getItemLayout is present and we scroll past render window Reviewed By: NickGerleman Differential Revision: D64222424 fbshipit-source-id: 7e22f377d2f754beb39fff2b5c097cea350daa7e --- .../virtualized-lists/Lists/VirtualizedList.js | 18 +++++++++++++++++- .../Lists/__tests__/VirtualizedList-test.js | 14 +++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 588e3ed9d3883c..4acdd08684c451 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -1160,7 +1160,7 @@ class VirtualizedList extends StateSafePureComponent { } componentDidUpdate(prevProps: Props) { - const {data, extraData} = this.props; + const {data, extraData, getItemLayout} = this.props; if (data !== prevProps.data || extraData !== prevProps.extraData) { // clear the viewableIndices cache to also trigger // the onViewableItemsChanged callback with the new data @@ -1181,6 +1181,14 @@ class VirtualizedList extends StateSafePureComponent { if (hiPriInProgress) { this._hiPriInProgress = false; } + + // We only call `onEndReached` after we render the last cell, but when + // getItemLayout is present, we can scroll past the last rendered cell, and + // never trigger a new layout or bounds change, so we need to check again + // after rendering more cells. + if (getItemLayout != null) { + this._maybeCallOnEdgeReached(); + } } _cellRefs: {[string]: null | CellRenderer} = {}; @@ -1496,6 +1504,14 @@ class VirtualizedList extends StateSafePureComponent { onEndReached, onEndReachedThreshold, } = this.props; + // Wait until we have real metrics + if ( + !this._listMetrics.hasContentLength() || + this._scrollMetrics.visibleLength === 0 + ) { + return; + } + // If we have any pending scroll updates it means that the scroll metrics // are out of date and we should not call any of the edge reached callbacks. if (this.state.pendingScrollUpdateCount > 0) { diff --git a/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js b/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js index 78648590d690d0..1c7f7f92dc4cc4 100644 --- a/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js +++ b/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js @@ -663,11 +663,6 @@ describe('VirtualizedList', () => { renderItem: ({item}) => , getItem: (items, index) => items[index], getItemCount: items => items.length, - getItemLayout: (items, index) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, - index, - }), onEndReached, }; @@ -694,6 +689,15 @@ describe('VirtualizedList', () => { expect(onEndReached).not.toHaveBeenCalled(); await act(() => { + for (let i = 0; i < 20; ++i) { + simulateCellLayout(component, data, i, { + width: 10, + height: ITEM_HEIGHT, + x: 0, + y: i * ITEM_HEIGHT, + }); + } + instance._onScroll({ timeStamp: 1000, nativeEvent: {