Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions lib/core/getStartStopIndices.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,41 @@ test("initial variable-size lookup only measures the viewport", () => {
expect(measurements).toBe(25);
});

test.each([0, 25])(
"deep variable-size lookup with %i cached rows only measures through the viewport",
(cachedRowCount) => {
let measurements = 0;
const cachedBounds = createCachedBounds({
itemCount: 100000,
itemProps: {},
itemSize: (index) => {
measurements++;
return index % 2 === 0 ? 10 : 30;
}
});
if (cachedRowCount > 0) {
cachedBounds.get(cachedRowCount - 1);
}
measurements = 0;

const result = getStartStopIndices({
cachedBounds,
containerScrollOffset: 50000,
containerSize: 80,
itemCount: 100000,
overscanCount: 3
});

expect(result).toEqual({
startIndexVisible: 2500,
stopIndexVisible: 2503,
startIndexOverscan: 2497,
stopIndexOverscan: 2506
});
expect(measurements).toBe(2504 - cachedRowCount);
}
);

test("matches a linear lookup across boundaries, zero sizes and scroll directions", () => {
for (const sizes of [
[],
Expand Down
18 changes: 8 additions & 10 deletions lib/core/getStartStopIndices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,9 @@ export function getStartStopIndices({
)
);
} else {
// Search the measured prefix first. Grow the search exponentially only
// when scrolling beyond it, so an initial render doesn't measure the list.
// Binary search the measured prefix
let low = 0;
let high = Math.min(maxIndex, Math.max(0, cachedBounds.size - 1));
let step = 1;
while (high < maxIndex) {
const bounds = cachedBounds.get(high);
if (bounds.scrollOffset + bounds.size > containerScrollOffset) break;
low = high + 1;
high = Math.min(maxIndex, high + step);
step *= 2;
}

while (low < high) {
const middle = Math.floor((low + high) / 2);
Expand All @@ -69,6 +60,13 @@ export function getStartStopIndices({
}
}

// Extend sequentially beyond the measured prefix to avoid evaluating item sizes past the viewport
while (low < maxIndex) {
const bounds = cachedBounds.get(low);
if (bounds.scrollOffset + bounds.size > containerScrollOffset) break;
low++;
}

startIndexVisible = low;
stopIndexVisible = low;
while (stopIndexVisible < maxIndex) {
Expand Down
Loading