Skip to content

Commit

Permalink
Improve FlatList estimates for items not yet laid out
Browse files Browse the repository at this point in the history
Summary:
Changelog: [General][Fixed] Fixed accuracy of FlatList estimations to determine what elements are visible in the rendering window.

This fixes a bug in FlatList where it thinks that some elements are visible in the rendering window, when they're not. Specifically, if a cell hasn't been laid out yet, it ignores all the information it already has on the ones that had, and estimates its position and offset based on the estimated size of the cells. In this case, if the first element has a larger offset because the list has a header, that offset is ignored in this case.

One observed result of this is that in a list where there's a header and a single cell that occupy the whole rendering window, FlatList thinks it needs to pre-render an additional element because the header is ignored.

Differential Revision: D62649060
  • Loading branch information
rubennorte authored and facebook-github-bot committed Sep 13, 2024
1 parent d7884a6 commit dd58621
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 6 deletions.
26 changes: 25 additions & 1 deletion packages/virtualized-lists/Lists/ListMetricsAggregator.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,38 @@ export default class ListMetricsAggregator {
// check for invalid frames due to row re-ordering
return frame;
} else {
let offset;

if (index > 0 && !this._orientation.rtl) {
// If we have more accurate measurements for the cells that have been
// measured already, we should use them. This is important because if
// the list has a header, the initial cell will have a larger offset
// that we should take into account here.
const highestMeasuredCellIndex = this.getHighestMeasuredCellIndex();
const highestMeasuredCellFrame = this.getCellMetrics(
highestMeasuredCellIndex,
props,
);
if (highestMeasuredCellFrame) {
offset =
highestMeasuredCellFrame.offset +
highestMeasuredCellFrame.length +
this._averageCellLength * (index - highestMeasuredCellIndex - 1);
}
}

if (offset == null) {
offset = this._averageCellLength * index;
}

const {data, getItemCount} = props;
invariant(
index >= 0 && index < getItemCount(data),
'Tried to get frame for out of range index ' + index,
);
return {
length: this._averageCellLength,
offset: this._averageCellLength * index,
offset,
index,
isMounted: false,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ describe('ListMetricsAggregator', () => {
height: 10,
width: 5,
x: 0,
y: 0,
y: 100,
},
});

Expand All @@ -233,15 +233,15 @@ describe('ListMetricsAggregator', () => {
height: 20,
width: 5,
x: 0,
y: 10,
y: 110,
},
});

expect(listMetrics.getCellMetrics(2, props)).toBeNull();
expect(listMetrics.getCellMetricsApprox(2, props)).toEqual({
index: 2,
length: 15,
offset: 30,
offset: 130,
isMounted: false,
});
});
Expand Down Expand Up @@ -481,7 +481,7 @@ describe('ListMetricsAggregator', () => {

it('estimates RTL metrics of unmeasured cell', () => {
const listMetrics = new ListMetricsAggregator();
const orientation = {horizontal: true, rtl: false};
const orientation = {horizontal: true, rtl: true};
const props: CellMetricProps = {
data: [1, 2, 3, 4, 5],
getItemCount: () => nullthrows(props.data).length,
Expand Down Expand Up @@ -528,7 +528,7 @@ describe('ListMetricsAggregator', () => {

it('uses getItemLayout for RTL metrics of unmeasured cell', () => {
const listMetrics = new ListMetricsAggregator();
const orientation = {horizontal: true, rtl: false};
const orientation = {horizontal: true, rtl: true};
const props: CellMetricProps = {
data: [1, 2, 3, 4, 5],
getItemCount: () => nullthrows(props.data).length,
Expand Down

0 comments on commit dd58621

Please sign in to comment.