From 2581c0829e32d30502a2e524d9bfc124aa81408e Mon Sep 17 00:00:00 2001 From: inokawa <48897392+inokawa@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:21:49 +0900 Subject: [PATCH] Improve timeout handling on imperative scroll when document is inactive --- src/core/scroller.ts | 12 +++++++++--- src/core/store.ts | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/scroller.ts b/src/core/scroller.ts index dd48b23e6..2f34a1828 100644 --- a/src/core/scroller.ts +++ b/src/core/scroller.ts @@ -200,13 +200,19 @@ export const createScroller = ( const waitForMeasurement = (): [Promise, () => void] => { // Wait for the scroll destination items to be measured. // The measurement will be done asynchronously and the timing is not predictable so we use promise. - // For example, ResizeObserver may not fire when window is not visible. let queue: (() => void) | undefined; return [ new Promise((resolve, reject) => { queue = resolve; - // Reject when items around scroll destination completely measured - timeout((cancelScroll = reject), 150); + cancelScroll = reject; + + // Resize event may not happen when the window/tab is not visible, or during browser back in Safari. + // We have to wait for the initial measurement to avoid failing imperative scroll on mount. + // https://github.com/inokawa/virtua/issues/450 + if (store._isInitialMeasurementDone()) { + // Reject when items around scroll destination completely measured + timeout(reject, 150); + } }), store._subscribe(UPDATE_SIZE_EVENT, () => { queue && queue(); diff --git a/src/core/store.ts b/src/core/store.ts index c0b6a34dc..895d4084f 100644 --- a/src/core/store.ts +++ b/src/core/store.ts @@ -117,6 +117,7 @@ export type VirtualStore = { _getCacheSnapshot(): CacheSnapshot; _getRange(): ItemsRange; _isUnmeasuredItem(index: number): boolean; + _isInitialMeasurementDone(): boolean; _hasUnmeasuredItemsInFrozenRange(): boolean; _getItemOffset(index: number): number; _getItemSize(index: number): number; @@ -216,6 +217,9 @@ export const createVirtualStore = ( _isUnmeasuredItem(index) { return cache._sizes[index] === UNCACHED; }, + _isInitialMeasurementDone() { + return !!viewportSize; + }, _hasUnmeasuredItemsInFrozenRange() { if (!_frozenRange) return false; return cache._sizes