Repository navigation
fix(virtual-core): request deferred ResizeObserver frames from the scroll element's window - #1320
Conversation
…roll element's window
🦋 Changeset detectedLatest commit: a51a749 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
piecyk
left a comment
There was a problem hiding this comment.
Thanks, this looks good. The three new tests fail on main and pass here. The cancel-on-cleanup path is safe because _willUpdate re-observes cached elements after cleanup, so an initial ResizeObserver entry re-delivers any dropped measurement.
Out of curiosity, what made you reach for useAnimationFrameWithResizeObserver in the popup case? Were you hitting the "ResizeObserver loop completed with undelivered notifications" error, or something else?
The option delays every measurement by a frame, so I'd like to understand the real-world reason people turn it on. If it's the loop error, we might be able to fix that at the source so the option isn't needed.
|
View your CI Pipeline Execution ↗ for commit a51a749
☁️ Nx Cloud last updated this comment at |
Thanks for checking! Some context on why we care: our app is multi-window by design (both in the browser and in Electron). It opens child windows with Routing everything through the scroll element's window is a big part of why TanStack Virtual fits us, so we audited the core for anything still tied to the global window. We don't enable If you end up fixing the loop error at the source and retiring the option, that works for us too, since these calls would go away with it. |
|
@NotYoojun Thanks for the context, that makes sense. Nice to hear the window routing is what drew you in; keeping everything on the scroll element's window is exactly the intent. Merging this, and I'll keep the option-retirement idea on the list. |
🎯 Changes
With
useAnimationFrameWithResizeObserver: true, both ResizeObserver callbacks defer their work with the global requestAnimationFrame:observeElementRect, for the scroll element's sizemeasureElementAll other window-bound APIs in the virtualizer go through
instance.targetWindow(scrollElement.ownerDocument.defaultView):ResizeObserver, timers,performance.now(), and thescrollToIndexreconcile frame. These two calls were the only exceptions.Problem 1: frames come from the wrong window. A same-origin popup opened with
window.open()shares the opener's JS realm, so the globalrequestAnimationFramebelongs to the opener. When a list is rendered into the popup (for example withcreatePortal), its deferred measurements wait on the opener's frames. Browsers stop those frames while the opener is minimized or fully occluded, so row measurements and the viewport size in the popup stop updating until the opener is visible again.Problem 2: queued frames are never cancelled. Suppose the scroll element changes while a frame is pending, for example when a panel moves into a popup and the virtualizer instance survives. The old element's rect is then applied after the new element's initial rect and overwrites
scrollRect. Item entries from a disconnected observer are still processed in the same way.Fix. A small internal
createFrameQueue(targetWindow)requests frames from the scroll element's window and tracks their ids.observeElementRectcancels its pending frames when it unsubscribes. The item observer cancels them indisconnect(), whichcleanup()calls.With the option off (the default), nothing changes. With it on and the list in the main window, the only difference is that pending frames are cancelled on teardown.
Tests. Three unit tests in
packages/virtual-core/tests/index.test.tsuse a mock window separate from the global one. All three fail onmainand pass with this change:observeElementRectdefers to the scroll element's window and cancels the pending frame on unsubscribe✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit