Skip to content

Commit f8e860a

Browse files
committed
perf(virtual-core): skip sync DOM reads during normal scrolling
1 parent 54d771a commit f8e860a

1 file changed

Lines changed: 31 additions & 31 deletions

File tree

packages/virtual-core/src/index.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,21 @@ export class Virtualizer<
409409
return (_ro = new this.targetWindow.ResizeObserver((entries) => {
410410
entries.forEach((entry) => {
411411
const run = () => {
412-
this._measureElement(entry.target as TItemElement, entry)
412+
const node = entry.target as TItemElement
413+
const index = this.indexFromElement(node)
414+
415+
if (!node.isConnected) {
416+
this.observer.unobserve(node)
417+
this.elementsCache.delete(this.options.getItemKey(index))
418+
return
419+
}
420+
421+
if (this.shouldMeasureDuringScroll(index)) {
422+
this.resizeItem(
423+
index,
424+
this.options.measureElement(node, entry, this),
425+
)
426+
}
413427
}
414428
this.options.useAnimationFrameWithResizeObserver
415429
? requestAnimationFrame(run)
@@ -984,21 +998,19 @@ export class Virtualizer<
984998
return true
985999
}
9861000

987-
private _measureElement = (
988-
node: TItemElement,
989-
entry: ResizeObserverEntry | undefined,
990-
) => {
991-
if (!node.isConnected) {
992-
this.observer.unobserve(node)
1001+
measureElement = (node: TItemElement | null) => {
1002+
if (!node) {
1003+
this.elementsCache.forEach((cached, key) => {
1004+
if (!cached.isConnected) {
1005+
this.observer.unobserve(cached)
1006+
this.elementsCache.delete(key)
1007+
}
1008+
})
9931009
return
9941010
}
9951011

9961012
const index = this.indexFromElement(node)
997-
const item = this.measurementsCache[index]
998-
if (!item) {
999-
return
1000-
}
1001-
const key = item.key
1013+
const key = this.options.getItemKey(index)
10021014
const prevNode = this.elementsCache.get(key)
10031015

10041016
if (prevNode !== node) {
@@ -1009,16 +1021,18 @@ export class Virtualizer<
10091021
this.elementsCache.set(key, node)
10101022
}
10111023

1012-
if (this.shouldMeasureDuringScroll(index)) {
1013-
this.resizeItem(index, this.options.measureElement(node, entry, this))
1024+
// Only sync-measure during programmatic scrolling (scrollToIndex/scrollToOffset)
1025+
// where reconcileScroll needs measurements in the same frame.
1026+
// During normal user scrolling, the RO callback handles it async.
1027+
if (this.scrollState && this.shouldMeasureDuringScroll(index)) {
1028+
this.resizeItem(index, this.options.measureElement(node, undefined, this))
10141029
}
10151030
}
10161031

10171032
resizeItem = (index: number, size: number) => {
10181033
const item = this.measurementsCache[index]
1019-
if (!item) {
1020-
return
1021-
}
1034+
if (!item) return
1035+
10221036
const itemSize = this.itemSizeCache.get(item.key) ?? item.size
10231037
const delta = size - itemSize
10241038

@@ -1045,20 +1059,6 @@ export class Virtualizer<
10451059
}
10461060
}
10471061

1048-
measureElement = (node: TItemElement | null | undefined) => {
1049-
if (!node) {
1050-
this.elementsCache.forEach((cached, key) => {
1051-
if (!cached.isConnected) {
1052-
this.observer.unobserve(cached)
1053-
this.elementsCache.delete(key)
1054-
}
1055-
})
1056-
return
1057-
}
1058-
1059-
this._measureElement(node, undefined)
1060-
}
1061-
10621062
getVirtualItems = memo(
10631063
() => [this.getVirtualIndexes(), this.getMeasurements()],
10641064
(indexes, measurements) => {

0 commit comments

Comments
 (0)