Skip to content

Commit

Permalink
fix: calculateRange on every update when not scrolling (#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
piecyk authored Nov 7, 2022
1 parent 125bb9c commit be7329f
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 6 deletions.
16 changes: 15 additions & 1 deletion packages/react-virtual/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ test('should render given dynamic size', async () => {
expect(renderer).toHaveBeenCalledTimes(3)
})

test.only('should render given dynamic size after scroll', () => {
test('should render given dynamic size after scroll', () => {
render(<List itemSize={100} dynamic />)

expect(screen.queryByText('Row 0')).toBeInTheDocument()
Expand Down Expand Up @@ -140,3 +140,17 @@ test('should use rangeExtractor', () => {
expect(screen.queryByText('Row 1')).toBeInTheDocument()
expect(screen.queryByText('Row 2')).not.toBeInTheDocument()
})

test('should handle count change', () => {
const { rerender } = render(<List count={2} />)

expect(screen.queryByText('Row 0')).toBeInTheDocument()
expect(screen.queryByText('Row 1')).toBeInTheDocument()
expect(screen.queryByText('Row 2')).not.toBeInTheDocument()

rerender(<List count={10} />)

expect(screen.queryByText('Row 2')).toBeInTheDocument()
expect(screen.queryByText('Row 4')).toBeInTheDocument()
expect(screen.queryByText('Row 5')).not.toBeInTheDocument()
})
34 changes: 29 additions & 5 deletions packages/virtual-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,15 @@ export interface VirtualizerOptions<
rangeExtractor?: (range: Range) => number[]
enableSmoothScroll?: boolean
scrollMargin?: number
scrollingDelay?: number
}

export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
private unsubs: (void | (() => void))[] = []
options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>
scrollElement: TScrollElement | null = null
isScrolling: boolean = false
private isScrollingTimeoutId: ReturnType<typeof setTimeout> | null = null
private measurementsCache: Item[] = []
private itemMeasurementsCache: Record<Key, number> = {}
private pendingMeasuredCacheIndexes: number[] = []
Expand All @@ -278,7 +281,7 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
number,
(measurableItem: TItemElement | null) => void
> = {}
private range: { startIndex: number; endIndex: number } = {
range: { startIndex: number; endIndex: number } = {
startIndex: 0,
endIndex: 0,
}
Expand Down Expand Up @@ -312,6 +315,7 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
measureElement,
initialRect: { width: 0, height: 0 },
scrollMargin: 0,
scrollingDelay: 150,
...opts,
}
}
Expand Down Expand Up @@ -353,10 +357,30 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {

this.unsubs.push(
this.options.observeElementOffset(this, (offset) => {
this.scrollOffset = offset
if (this.isScrollingTimeoutId !== null) {
clearTimeout(this.isScrollingTimeoutId)
this.isScrollingTimeoutId = null
}

if (this.scrollOffset !== offset) {
this.isScrolling = true
this.scrollOffset = offset

this.isScrollingTimeoutId = setTimeout(() => {
this.isScrollingTimeoutId = null
this.isScrolling = false

this.notify()
}, this.options.scrollingDelay)
} else {
this.isScrolling = false
}

this.calculateRange()
}),
)
} else if (!this.isScrolling) {
this.calculateRange()
}
}

Expand Down Expand Up @@ -403,7 +427,7 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
},
)

private calculateRange = memo(
calculateRange = memo(
() => [this.getMeasurements(), this.getSize(), this.scrollOffset],
(measurements, outerSize, scrollOffset) => {
const range = calculateRange({
Expand Down Expand Up @@ -591,12 +615,12 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {

private _scrollToOffset = (
offset: number,
{ canSmooth, sync }: { canSmooth: boolean; sync: boolean },
options: { canSmooth: boolean; sync: boolean },
) => {
clearTimeout(this.scrollCheckFrame)

this.destinationOffset = offset
this.options.scrollToFn(offset, { canSmooth, sync }, this)
this.options.scrollToFn(offset, options, this)

let scrollCheckFrame: ReturnType<typeof setTimeout>

Expand Down

0 comments on commit be7329f

Please sign in to comment.