fix: defer sentinel check until masonry container has a measured width#736
Merged
Conversation
When hasMore=true, the scroll sentinel effect called check() immediately on mount. At that point masonryWidth=0 (the ResizeObserver hasn't fired yet) so the sentinel sits at top≈0, check() returns true, and onLoadMore fires before any cards are visible. This triggers an immediate second-page fetch that produces the pagination flash. Fix: move the masonryRef/columnWidth/useContainerPosition declarations above the sentinel effect so masonryWidth is in scope, then add masonryWidth to the effect deps and return early when it is 0. The sentinel first runs once the masonry container has a real width, at which point the sentinel is at its true position and check() correctly reflects whether the user needs more content. The positioner useMemo and all other masonry logic are unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Verifies that onLoadMore is not called while masonryWidth=0 (the ResizeObserver hasn't fired yet). Uses a real setTimeout to give React's MessageChannel-based scheduler time to fire mount effects before asserting. Fails on the pre-fix code, passes with the guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Fixes #734 (Bug 1). On the PieceList page with 17+ pieces, a second-page fetch fires immediately on mount — before the user has scrolled and before any cards are visible — causing a dark flash as the masonry re-lays out.
Root cause: The scroll sentinel
useEffectcalledcheck()immediately whenhasMorebecametrue. At that momentmasonryWidth=0(theuseContainerPositionResizeObserver hasn't fired yet), so the sentinel sits attop≈0in the unmeasured document.check()seestop ≤ window.innerHeight + 300→ callsonLoadMore→ page 2 fetch starts before page 1 is painted.Fix
Move the
masonryRef/columnWidth/useContainerPositiondeclarations above the sentinel effect somasonryWidthis in scope, then add it to the effect's dependency list and return early whenmasonryWidth === 0:The sentinel now first runs once the masonry container has a real width — at which point the sentinel is at its true document position and
check()correctly reflects whether the user actually needs more content.The
positioneruseMemoand all other masonry logic are unchanged.Regression Test
PieceList > scroll sentinel > does not call onLoadMore while the masonry container width is unmeasuredRenders 16 pieces with
hasMore=trueandmasonryWidth=0, then waits 50 ms (giving React'sMessageChannel-based scheduler time to fire mount effects) and assertsonLoadMorewas never called.mainbefore this fix —onLoadMoreis called 1 timeonLoadMorenever calledVerification
Closes #734