From 22a4205bdd63d60c7d7290693aaccfe47e84ebb3 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 28 Mar 2017 19:59:20 -0700 Subject: [PATCH] onEndReached should clear when data changes Summary: it's possible to update data and not have the content length change, which could prevent onEndReached from ever firing again, so fix that. Reviewed By: bvaughn, yungsters Differential Revision: D4783818 fbshipit-source-id: ec4640f4b8cf820165b045eaafee6fb41c0b0499 --- Libraries/Lists/FlatList.js | 4 ++-- Libraries/Lists/SectionList.js | 6 ++++++ Libraries/Lists/VirtualizedList.js | 20 +++++++++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index 15cbcc43740..f2a7fc52404 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -63,8 +63,8 @@ type OptionalProps = { columnWrapperStyle?: StyleObj, /** * A marker property for telling the list to re-render (since it implements `PureComponent`). If - * your `renderItem` function depends on anything outside of the `data` prop, stick it here and - * treat it immutably. + * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the + * `data` prop, stick it here and treat it immutably. */ extraData?: any, /** diff --git a/Libraries/Lists/SectionList.js b/Libraries/Lists/SectionList.js index cb875b3dd2d..f40c978d53e 100644 --- a/Libraries/Lists/SectionList.js +++ b/Libraries/Lists/SectionList.js @@ -60,6 +60,12 @@ type OptionalProps> = { * Rendered in between each section. */ SectionSeparatorComponent?: ?ReactClass, + /** + * A marker property for telling the list to re-render (since it implements `PureComponent`). If + * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the + * `data` prop, stick it here and treat it immutably. + */ + extraData?: any, /** * How many items to render in the initial batch. This should be enough to fill the screen but not * much more. Note these items will never be unmounted as part of the windowed rendering in order diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 509971609de..4e039729d6c 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -51,8 +51,8 @@ type OptionalProps = { disableVirtualization: boolean, /** * A marker property for telling the list to re-render (since it implements `PureComponent`). If - * your `renderItem` function depends on anything outside of the `data` prop, stick it here and - * treat it immutably. + * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the + * `data` prop, stick it here and treat it immutably. */ extraData?: any, /** @@ -307,13 +307,16 @@ class VirtualizedList extends React.PureComponent { } componentWillReceiveProps(newProps: Props) { - const {data, getItemCount, maxToRenderPerBatch} = newProps; + const {data, extraData, getItemCount, maxToRenderPerBatch} = newProps; // first and last could be stale (e.g. if a new, shorter items props is passed in), so we make // sure we're rendering a reasonable range here. this.setState({ first: Math.max(0, Math.min(this.state.first, getItemCount(data) - 1 - maxToRenderPerBatch)), last: Math.max(0, Math.min(this.state.last, getItemCount(data) - 1)), }); + if (data !== this.props.data || extraData !== this.props.extraData) { + this._hasDataChangedSinceEndReached = true; + } this._updateCellsToRenderBatcher.schedule(); } @@ -462,6 +465,7 @@ class VirtualizedList extends React.PureComponent { } _averageCellLength = 0; + _hasDataChangedSinceEndReached = true; _hasWarned = {}; _highestMeasuredFrameIndex = 0; _headerLength = 0; @@ -619,10 +623,12 @@ class VirtualizedList extends React.PureComponent { } const distanceFromEnd = contentLength - visibleLength - offset; const itemCount = getItemCount(data); - if (distanceFromEnd < onEndReachedThreshold * visibleLength && - this._scrollMetrics.contentLength !== this._sentEndForContentLength && - this.state.last === itemCount - 1) { - // Only call onEndReached for a given content length once. + if (this.state.last === itemCount - 1 && + distanceFromEnd < onEndReachedThreshold * visibleLength && + (this._hasDataChangedSinceEndReached || + this._scrollMetrics.contentLength !== this._sentEndForContentLength)) { + // Only call onEndReached once for a given dataset + content length. + this._hasDataChangedSinceEndReached = false; this._sentEndForContentLength = this._scrollMetrics.contentLength; onEndReached({distanceFromEnd}); }