From 65d32fafe404ab12d5cc69a14e09c95f5b19731e Mon Sep 17 00:00:00 2001 From: ifsnow Date: Sun, 15 Sep 2019 23:26:25 +0900 Subject: [PATCH 1/6] Fix `onEndReached` for FlatList --- Libraries/Lists/VirtualizedList.js | 55 ++++++++++++++++++------------ 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 62f614ec06e497..5024667a3093db 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -1077,8 +1077,6 @@ class VirtualizedList extends React.PureComponent { componentDidUpdate(prevProps: Props) { const {data, extraData} = this.props; if (data !== prevProps.data || extraData !== prevProps.extraData) { - this._hasDataChangedSinceEndReached = true; - // clear the viewableIndices cache to also trigger // the onViewableItemsChanged callback with the new data this._viewabilityTuples.forEach(tuple => { @@ -1107,7 +1105,6 @@ class VirtualizedList extends React.PureComponent { _fillRateHelper: FillRateHelper; _frames = {}; _footerLength = 0; - _hasDataChangedSinceEndReached = true; _hasDoneInitialScroll = false; _hasInteracted = false; _hasMore = false; @@ -1137,6 +1134,7 @@ class VirtualizedList extends React.PureComponent { _totalCellsMeasured = 0; _updateCellsToRenderBatcher: Batchinator; _viewabilityTuples: Array = []; + _nextExpectedOnEndReachedOffset = 0; _captureScrollRef = ref => { this._scrollRef = ref; @@ -1372,29 +1370,42 @@ class VirtualizedList extends React.PureComponent { } _maybeCallOnEndReached() { - const { - data, - getItemCount, - onEndReached, - onEndReachedThreshold, - } = this.props; - const {contentLength, visibleLength, offset} = this._scrollMetrics; + const {onEndReached, onEndReachedThreshold} = this.props; + if (!onEndReached) { + return; + } + + const {contentLength, visibleLength, offset, dOffset} = this._scrollMetrics; + // Scrolled in a direction that doesn't require a check, + // such as scrolling up in the vertical list + if (offset <= 0 || dOffset <= 0) { + return; + } + + // contentLength did not change because no new data was added + if (contentLength === this._sentEndForContentLength) { + return; + } + const distanceFromEnd = contentLength - visibleLength - offset; + + // $FlowFixMe + const minimumDistanceFromEnd = onEndReachedThreshold * visibleLength; + if (distanceFromEnd >= minimumDistanceFromEnd) { + return; + } + + // Not as scrolling as expected after the last `onEndReached` call if ( - onEndReached && - this.state.last === getItemCount(data) - 1 && - /* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an - * error found when Flow v0.63 was deployed. To see the error delete this - * comment and run Flow. */ - distanceFromEnd < onEndReachedThreshold * visibleLength && - (this._hasDataChangedSinceEndReached || - this._scrollMetrics.contentLength !== this._sentEndForContentLength) + this._nextExpectedOnEndReachedOffset > 0 && + offset < this._nextExpectedOnEndReachedOffset ) { - // Only call onEndReached once for a given dataset + content length. - this._hasDataChangedSinceEndReached = false; - this._sentEndForContentLength = this._scrollMetrics.contentLength; - onEndReached({distanceFromEnd}); + return; } + + this._sentEndForContentLength = this._scrollMetrics.contentLength; + this._nextExpectedOnEndReachedOffset = offset + minimumDistanceFromEnd / 2; + onEndReached({distanceFromEnd}); } _onContentSizeChange = (width: number, height: number) => { From 6600cbb68a212a9fd2ade10832fb0a17f4c0e265 Mon Sep 17 00:00:00 2001 From: ifsnow Date: Tue, 17 Sep 2019 14:13:51 +0900 Subject: [PATCH 2/6] Improve logic more easily --- Libraries/Lists/VirtualizedList.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 5024667a3093db..263baad625b3be 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -1134,7 +1134,6 @@ class VirtualizedList extends React.PureComponent { _totalCellsMeasured = 0; _updateCellsToRenderBatcher: Batchinator; _viewabilityTuples: Array = []; - _nextExpectedOnEndReachedOffset = 0; _captureScrollRef = ref => { this._scrollRef = ref; @@ -1376,6 +1375,7 @@ class VirtualizedList extends React.PureComponent { } const {contentLength, visibleLength, offset, dOffset} = this._scrollMetrics; + // Scrolled in a direction that doesn't require a check, // such as scrolling up in the vertical list if (offset <= 0 || dOffset <= 0) { @@ -1389,22 +1389,18 @@ class VirtualizedList extends React.PureComponent { const distanceFromEnd = contentLength - visibleLength - offset; - // $FlowFixMe - const minimumDistanceFromEnd = onEndReachedThreshold * visibleLength; - if (distanceFromEnd >= minimumDistanceFromEnd) { + // If the distance is farther than can be seen on the screen + if (distanceFromEnd >= visibleLength) { return; } - // Not as scrolling as expected after the last `onEndReached` call - if ( - this._nextExpectedOnEndReachedOffset > 0 && - offset < this._nextExpectedOnEndReachedOffset - ) { + // $FlowFixMe + const minimumDistanceFromEnd = onEndReachedThreshold * visibleLength; + if (distanceFromEnd >= minimumDistanceFromEnd) { return; } - this._sentEndForContentLength = this._scrollMetrics.contentLength; - this._nextExpectedOnEndReachedOffset = offset + minimumDistanceFromEnd / 2; + this._sentEndForContentLength = contentLength; onEndReached({distanceFromEnd}); } From 43744979347085afdc60f78ecaff31426e4c2192 Mon Sep 17 00:00:00 2001 From: ifsnow Date: Tue, 17 Sep 2019 14:27:44 +0900 Subject: [PATCH 3/6] Add a test for onEndReached --- .../Lists/__tests__/VirtualizedList-test.js | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index c4873374e0ef54..80f9988655d28a 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -273,4 +273,149 @@ describe('VirtualizedList', () => { }), ); }); + + it('OnEndReached should not be called after initial rendering', () => { + const ITEM_HEIGHT = 100; + const APPENDED_ITEM_COUNT = 10; + const data = appendNewItems([], 10); + const onEndReached = jest.fn(function() { + appendNewItems(data, APPENDED_ITEM_COUNT); + }); + + const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); + + const component = ReactTestRenderer.create(); + const instance = component.getInstance(); + + const scroll = createScrollMethod(instance, ITEM_HEIGHT); + scroll(data, 0); + + expect(onEndReached).not.toHaveBeenCalled(); + expect(data.length).toBe(10); + }); + + it('OnEndReached should be called once after scrolling by 200', () => { + const ITEM_HEIGHT = 100; + const APPENDED_ITEM_COUNT = 10; + const data = appendNewItems([], 10); + const onEndReached = jest.fn(function() { + appendNewItems(data, APPENDED_ITEM_COUNT); + }); + + const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); + + const component = ReactTestRenderer.create(); + const instance = component.getInstance(); + + const scroll = createScrollMethod(instance, ITEM_HEIGHT); + scroll(data, 200); + + expect(onEndReached).toHaveBeenCalledTimes(1); + expect(onEndReached).toHaveBeenLastCalledWith({ + distanceFromEnd: 200, + }); + expect(data.length).toBe(20); + }); + + it('OnEndReached should not be called twice in a short period while scrolling fast', () => { + const ITEM_HEIGHT = 100; + const APPENDED_ITEM_COUNT = 10; + const data = appendNewItems([], 10); + const onEndReached = jest.fn(function() { + appendNewItems(data, APPENDED_ITEM_COUNT); + }); + + const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); + + const component = ReactTestRenderer.create(); + const instance = component.getInstance(); + + const scroll = createScrollMethod(instance, ITEM_HEIGHT); + scroll(data, 200); + scroll(data, 300, 50); + + expect(onEndReached).toHaveBeenCalledTimes(1); + expect(onEndReached).toHaveBeenLastCalledWith({ + distanceFromEnd: 200, + }); + expect(data.length).toBe(20); + }); + + it('OnEndReached should be called when required to load more items', () => { + const ITEM_HEIGHT = 100; + const APPENDED_ITEM_COUNT = 10; + const data = appendNewItems([], 10); + const onEndReached = jest.fn(function() { + appendNewItems(data, APPENDED_ITEM_COUNT); + }); + + const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); + + const component = ReactTestRenderer.create(); + const instance = component.getInstance(); + + const scroll = createScrollMethod(instance, ITEM_HEIGHT); + scroll(data, 200); + expect(onEndReached).toHaveBeenCalledTimes(1); + expect(data.length).toBe(20); + + scroll(data, 1000); + expect(onEndReached).toHaveBeenCalledTimes(2); + expect(onEndReached).toHaveBeenLastCalledWith({ + distanceFromEnd: 400, + }); + expect(data.length).toBe(30); + }); }); + +function createPropsWithonEndReached(data, itemHeight, onEndReached) { + const props = { + data, + renderItem: ({item}) => , + getItem: (items, index) => items[index], + getItemCount: items => items.length, + getItemLayout: (items, index) => ({ + length: itemHeight, + offset: itemHeight * index, + index, + }), + onEndReached, + }; + + return props; +} + +function appendNewItems(items, count) { + const nextId = (items.length > 0 ? items[items.length - 1].id : 0) + 1; + + for (let loop = 1; loop <= count; loop++) { + const id = nextId + loop; + items.push({ + id: id, + key: `k${id}`, + }); + } + + return items; +} + +function createScrollMethod(instance, itemHeight) { + let scrollTimeStamp = 0; + + return function scroll(items, y, delay = 1000) { + scrollTimeStamp += delay; + + const nativeEvent = { + contentOffset: {y, x: 0}, + layoutMeasurement: {width: 300, height: 600}, + contentSize: {width: 300, height: items.length * itemHeight}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }; + + instance._onScroll({ + timeStamp: scrollTimeStamp, + nativeEvent, + }); + }; +} From 5451f8e88eec87813a63fd1c407f4d35c50bcc8d Mon Sep 17 00:00:00 2001 From: ifsnow Date: Mon, 23 Sep 2019 17:37:22 +0900 Subject: [PATCH 4/6] Improve a test for onEndReached --- .../Lists/__tests__/VirtualizedList-test.js | 188 ++++++++---------- 1 file changed, 79 insertions(+), 109 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index 80f9988655d28a..e795dbcc8336ea 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -273,149 +273,119 @@ describe('VirtualizedList', () => { }), ); }); +}); - it('OnEndReached should not be called after initial rendering', () => { - const ITEM_HEIGHT = 100; - const APPENDED_ITEM_COUNT = 10; - const data = appendNewItems([], 10); - const onEndReached = jest.fn(function() { - appendNewItems(data, APPENDED_ITEM_COUNT); - }); +describe('VirtualizedList > OnEndReached', () => { + const ITEM_HEIGHT = 100; + const APPENDED_ITEM_COUNT = 10; - const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); + let listItems, onEndReached, instance; - const component = ReactTestRenderer.create(); - const instance = component.getInstance(); + beforeEach(() => { + listItems = appendNewItems([], 10); - const scroll = createScrollMethod(instance, ITEM_HEIGHT); - scroll(data, 0); + onEndReached = jest.fn(function() { + appendNewItems(listItems, APPENDED_ITEM_COUNT); + }); - expect(onEndReached).not.toHaveBeenCalled(); - expect(data.length).toBe(10); + instance = createComponentInstance(); }); - it('OnEndReached should be called once after scrolling by 200', () => { - const ITEM_HEIGHT = 100; - const APPENDED_ITEM_COUNT = 10; - const data = appendNewItems([], 10); - const onEndReached = jest.fn(function() { - appendNewItems(data, APPENDED_ITEM_COUNT); - }); - - const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); + it('should not be called after initial rendering', () => { + const scroll = createScrollMethod(); + scroll(0); - const component = ReactTestRenderer.create(); - const instance = component.getInstance(); + expect(onEndReached).not.toHaveBeenCalled(); + expect(listItems.length).toBe(10); + }); - const scroll = createScrollMethod(instance, ITEM_HEIGHT); - scroll(data, 200); + it('should be called once after scrolling by 200', () => { + const scroll = createScrollMethod(); + scroll(200); expect(onEndReached).toHaveBeenCalledTimes(1); expect(onEndReached).toHaveBeenLastCalledWith({ distanceFromEnd: 200, }); - expect(data.length).toBe(20); + expect(listItems.length).toBe(20); }); - it('OnEndReached should not be called twice in a short period while scrolling fast', () => { - const ITEM_HEIGHT = 100; - const APPENDED_ITEM_COUNT = 10; - const data = appendNewItems([], 10); - const onEndReached = jest.fn(function() { - appendNewItems(data, APPENDED_ITEM_COUNT); - }); - - const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); - - const component = ReactTestRenderer.create(); - const instance = component.getInstance(); - - const scroll = createScrollMethod(instance, ITEM_HEIGHT); - scroll(data, 200); - scroll(data, 300, 50); + it('should not be called twice in a short period while scrolling fast', () => { + const scroll = createScrollMethod(); + scroll(200); + scroll(300, 50); expect(onEndReached).toHaveBeenCalledTimes(1); expect(onEndReached).toHaveBeenLastCalledWith({ distanceFromEnd: 200, }); - expect(data.length).toBe(20); + expect(listItems.length).toBe(20); }); - it('OnEndReached should be called when required to load more items', () => { - const ITEM_HEIGHT = 100; - const APPENDED_ITEM_COUNT = 10; - const data = appendNewItems([], 10); - const onEndReached = jest.fn(function() { - appendNewItems(data, APPENDED_ITEM_COUNT); - }); - - const props = createPropsWithonEndReached(data, ITEM_HEIGHT, onEndReached); - - const component = ReactTestRenderer.create(); - const instance = component.getInstance(); - - const scroll = createScrollMethod(instance, ITEM_HEIGHT); - scroll(data, 200); + it('should be called when required to load more items', () => { + const scroll = createScrollMethod(); + scroll(200); expect(onEndReached).toHaveBeenCalledTimes(1); - expect(data.length).toBe(20); + expect(listItems.length).toBe(20); - scroll(data, 1000); + scroll(1000); expect(onEndReached).toHaveBeenCalledTimes(2); expect(onEndReached).toHaveBeenLastCalledWith({ distanceFromEnd: 400, }); - expect(data.length).toBe(30); + expect(listItems.length).toBe(30); }); -}); -function createPropsWithonEndReached(data, itemHeight, onEndReached) { - const props = { - data, - renderItem: ({item}) => , - getItem: (items, index) => items[index], - getItemCount: items => items.length, - getItemLayout: (items, index) => ({ - length: itemHeight, - offset: itemHeight * index, - index, - }), - onEndReached, - }; - - return props; -} - -function appendNewItems(items, count) { - const nextId = (items.length > 0 ? items[items.length - 1].id : 0) + 1; - - for (let loop = 1; loop <= count; loop++) { - const id = nextId + loop; - items.push({ - id: id, - key: `k${id}`, - }); + function createComponentInstance() { + const props = { + data: listItems, + renderItem: ({item}) => , + getItem: (items, index) => items[index], + getItemCount: items => items.length, + getItemLayout: (items, index) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index, + }), + onEndReached, + }; + + const component = ReactTestRenderer.create(); + return component.getInstance(); + } + + function appendNewItems(items, count) { + const nextId = (items.length > 0 ? items[items.length - 1].id : 0) + 1; + + for (let loop = 1; loop <= count; loop++) { + const id = nextId + loop; + items.push({ + id: id, + key: `k${id}`, + }); + } + + return items; } - return items; -} + function createScrollMethod() { + let scrollTimeStamp = 0; -function createScrollMethod(instance, itemHeight) { - let scrollTimeStamp = 0; + return function scroll(y, delay = 1000) { + scrollTimeStamp += delay; - return function scroll(items, y, delay = 1000) { - scrollTimeStamp += delay; + const nativeEvent = { + contentOffset: {y, x: 0}, + layoutMeasurement: {width: 300, height: 600}, + contentSize: {width: 300, height: listItems.length * ITEM_HEIGHT}, + zoomScale: 1, + contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + }; - const nativeEvent = { - contentOffset: {y, x: 0}, - layoutMeasurement: {width: 300, height: 600}, - contentSize: {width: 300, height: items.length * itemHeight}, - zoomScale: 1, - contentInset: {right: 0, top: 0, left: 0, bottom: 0}, + instance._onScroll({ + timeStamp: scrollTimeStamp, + nativeEvent, + }); }; - - instance._onScroll({ - timeStamp: scrollTimeStamp, - nativeEvent, - }); - }; -} + } +}); From a4ab11631f9f96c207108debc6f35cb41ad15ec4 Mon Sep 17 00:00:00 2001 From: ifsnow Date: Wed, 2 Oct 2019 13:44:49 +0900 Subject: [PATCH 5/6] Modify logic to work more clearly --- Libraries/Lists/VirtualizedList.js | 43 +++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 263baad625b3be..8df94b90d4f16b 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -1134,6 +1134,7 @@ class VirtualizedList extends React.PureComponent { _totalCellsMeasured = 0; _updateCellsToRenderBatcher: Batchinator; _viewabilityTuples: Array = []; + _hasDoneFirstScroll = false; _captureScrollRef = ref => { this._scrollRef = ref; @@ -1368,7 +1369,7 @@ class VirtualizedList extends React.PureComponent { return !this.props.horizontal ? metrics.y : metrics.x; } - _maybeCallOnEndReached() { + _maybeCallOnEndReached(hasShrinkedContentLength: boolean = false) { const {onEndReached, onEndReachedThreshold} = this.props; if (!onEndReached) { return; @@ -1376,21 +1377,29 @@ class VirtualizedList extends React.PureComponent { const {contentLength, visibleLength, offset, dOffset} = this._scrollMetrics; - // Scrolled in a direction that doesn't require a check, - // such as scrolling up in the vertical list - if (offset <= 0 || dOffset <= 0) { + // If this is just after the initial rendering + if ( + !hasShrinkedContentLength && + !this._hasDoneFirstScroll && + offset === 0 + ) { + return; + } + + // If scrolled up in the vertical list + if (dOffset < 0) { return; } - // contentLength did not change because no new data was added + // If contentLength has not changed if (contentLength === this._sentEndForContentLength) { return; } const distanceFromEnd = contentLength - visibleLength - offset; - // If the distance is farther than can be seen on the screen - if (distanceFromEnd >= visibleLength) { + // If the distance is so farther than the area shown on the screen + if (distanceFromEnd >= visibleLength * 1.5) { return; } @@ -1421,9 +1430,24 @@ class VirtualizedList extends React.PureComponent { if (this.props.onContentSizeChange) { this.props.onContentSizeChange(width, height); } - this._scrollMetrics.contentLength = this._selectLength({height, width}); + const {contentLength: currentContentLength} = this._scrollMetrics; + const contentLength = this._selectLength({height, width}); + this._scrollMetrics.contentLength = contentLength; this._scheduleCellsToRenderUpdate(); - this._maybeCallOnEndReached(); + + const hasShrinkedContentLength = + currentContentLength > 0 && + contentLength > 0 && + contentLength < currentContentLength; + + if ( + hasShrinkedContentLength && + this._sentEndForContentLength >= contentLength + ) { + this._sentEndForContentLength = 0; + } + + this._maybeCallOnEndReached(hasShrinkedContentLength); }; /* Translates metrics from a scroll event in a parent VirtualizedList into @@ -1510,6 +1534,7 @@ class VirtualizedList extends React.PureComponent { if (!this.props) { return; } + this._hasDoneFirstScroll = true; this._maybeCallOnEndReached(); if (velocity !== 0) { this._fillRateHelper.activate(); From 2d3f6ca9801ef92b446458a2efc795db4ec17021 Mon Sep 17 00:00:00 2001 From: ifsnow Date: Wed, 2 Oct 2019 13:45:14 +0900 Subject: [PATCH 6/6] Improve testing to check for more cases --- .../Lists/__tests__/VirtualizedList-test.js | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index e795dbcc8336ea..a9eb83f90f92d9 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -277,12 +277,16 @@ describe('VirtualizedList', () => { describe('VirtualizedList > OnEndReached', () => { const ITEM_HEIGHT = 100; + const INITIAL_ITEM_COUNT = 20; const APPENDED_ITEM_COUNT = 10; let listItems, onEndReached, instance; + let shrinkedItemHeight; beforeEach(() => { - listItems = appendNewItems([], 10); + shrinkedItemHeight = 0; + + listItems = appendNewItems([], INITIAL_ITEM_COUNT); onEndReached = jest.fn(function() { appendNewItems(listItems, APPENDED_ITEM_COUNT); @@ -292,48 +296,60 @@ describe('VirtualizedList > OnEndReached', () => { }); it('should not be called after initial rendering', () => { + expect(onEndReached).not.toHaveBeenCalled(); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT); + }); + + it('should be called when the item layout is shrinked', () => { + expect(onEndReached).not.toHaveBeenCalled(); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT); + + shrinkedItemHeight = ITEM_HEIGHT / 2; + const scroll = createScrollMethod(); scroll(0); - expect(onEndReached).not.toHaveBeenCalled(); - expect(listItems.length).toBe(10); + expect(onEndReached).toHaveBeenCalledTimes(1); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT + APPENDED_ITEM_COUNT); }); - it('should be called once after scrolling by 200', () => { + it('should be called once after scrolling by 800', () => { const scroll = createScrollMethod(); - scroll(200); + scroll(800); expect(onEndReached).toHaveBeenCalledTimes(1); expect(onEndReached).toHaveBeenLastCalledWith({ - distanceFromEnd: 200, + distanceFromEnd: 464, }); - expect(listItems.length).toBe(20); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT + APPENDED_ITEM_COUNT); }); it('should not be called twice in a short period while scrolling fast', () => { const scroll = createScrollMethod(); - scroll(200); - scroll(300, 50); + scroll(800); expect(onEndReached).toHaveBeenCalledTimes(1); expect(onEndReached).toHaveBeenLastCalledWith({ - distanceFromEnd: 200, + distanceFromEnd: 464, }); - expect(listItems.length).toBe(20); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT + APPENDED_ITEM_COUNT); + + scroll(850, 50); + expect(onEndReached).toHaveBeenCalledTimes(1); }); it('should be called when required to load more items', () => { const scroll = createScrollMethod(); - scroll(200); + scroll(800); expect(onEndReached).toHaveBeenCalledTimes(1); - expect(listItems.length).toBe(20); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT + APPENDED_ITEM_COUNT); - scroll(1000); + scroll(1600); expect(onEndReached).toHaveBeenCalledTimes(2); expect(onEndReached).toHaveBeenLastCalledWith({ - distanceFromEnd: 400, + distanceFromEnd: 664, }); - expect(listItems.length).toBe(30); + expect(listItems.length).toBe(INITIAL_ITEM_COUNT + APPENDED_ITEM_COUNT * 2); }); function createComponentInstance() { @@ -343,8 +359,8 @@ describe('VirtualizedList > OnEndReached', () => { getItem: (items, index) => items[index], getItemCount: items => items.length, getItemLayout: (items, index) => ({ - length: ITEM_HEIGHT, - offset: ITEM_HEIGHT * index, + length: shrinkedItemHeight ? shrinkedItemHeight : ITEM_HEIGHT, + offset: (shrinkedItemHeight ? shrinkedItemHeight : ITEM_HEIGHT) * index, index, }), onEndReached, @@ -376,8 +392,13 @@ describe('VirtualizedList > OnEndReached', () => { const nativeEvent = { contentOffset: {y, x: 0}, - layoutMeasurement: {width: 300, height: 600}, - contentSize: {width: 300, height: listItems.length * ITEM_HEIGHT}, + layoutMeasurement: {width: 414, height: 736}, + contentSize: { + width: 414, + height: + listItems.length * + (shrinkedItemHeight ? shrinkedItemHeight : ITEM_HEIGHT), + }, zoomScale: 1, contentInset: {right: 0, top: 0, left: 0, bottom: 0}, };