Skip to content

Fix masonry visibility so viewport-overlapping items are not omitted#2374

Open
momomuchu wants to merge 1 commit into
Shopify:mainfrom
momomuchu:fix/masonry-append-misalignment
Open

Fix masonry visibility so viewport-overlapping items are not omitted#2374
momomuchu wants to merge 1 commit into
Shopify:mainfrom
momomuchu:fix/masonry-append-misalignment

Conversation

@momomuchu

Copy link
Copy Markdown

Fix masonry visibility so viewport-overlapping items are not omitted

Bug

In a masonry FlashList, items that overlap the viewport can be omitted from render. Reported case: item 25, whose layout overlaps the viewport range 3600..4100, was not rendered.

Root cause

The base RVLayoutManager.getVisibleLayouts uses binarySearchVisibleIndex, which (per its own docstring) assumes the layouts array is pre-sorted by the relevant dimension. That holds for linear and grid layouts, but not for masonry: masonry assigns items round-robin to the shortest column, so layouts[i].y is not monotonic in index. A later index can sit at a smaller y than an earlier one. The binary search then skips over items that are actually on screen.

Fix

Override getVisibleLayouts on the masonry layout manager to compute visibility by actual layout overlap (layout.y and layout.y + height against the viewport) instead of the sorted-array binary search. It returns a contiguous ConsecutiveNumbers range from the first to the last visible masonry index, matching the existing render-stack contract shape.

The override lives only on the masonry subclass. Linear and grid layout managers extend the base class directly and are unaffected.

Tests

Added src/__tests__/MasonryLayoutManager.test.ts:

  1. Appended items that overlap the engaged range stay inside it (reproduces the reported omission of item 25).
  2. Masonry visibility is computed by layout overlap rather than sorted binary search.

Reverting only the source change makes the RED test fail with the exact reported symptom (missingVisibleIndices: [25]), so the test has teeth.

yarn jest MasonryLayoutManager   # 8 passed
yarn jest                        # 188 passed
yarn type-check                  # clean

Note on over-render

getVisibleLayouts must return a contiguous ConsecutiveNumbers range, so on masonry it returns the span from the first to the last visible index. When columns are reasonably balanced this includes at most a couple of off-screen indices. In a pathological case (one item far taller than its neighbors, so a visible item sits many indices away from another visible item) the span can include more off-screen indices, up to O(n) in the worst construction. That over-render is inherent to the contiguous return type, not new to this change: it is the minimum span that still contains every visible index. The key property is that no visible item is ever omitted. The previous binary-search behavior omitted the visible item in exactly that tall-item case (the bug being fixed here), so this is a strict correctness improvement, and the contiguous-range contract shape is unchanged.

If a maintainer prefers to cap the rendered span for very large masonry lists, a per-column visible-index computation would be a natural follow-up, but it is out of scope for this correctness fix.

Fixes #2270

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FlashList 2.2.0 [Masonry] misaligned items after appending data (pagination)

1 participant