fix(lazy): handle empty item content without breaking mount termination#42
Merged
Conversation
VirtualGrid calls withScrolling(false) at module init through the package barrel, but index.ts re-exports withScrolling after VirtualGrid, so when the barrel is the import entry the binding is still uninitialized and the call throws. Import the module directly instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ffset Items that render empty insert no child node, so offset() overstates the rendered edge and navigation stops mounting new items (#26). Use container.children.length for the proximity check (child units, matching container.selected) while keeping termination on offset() >= maxOffset (data units) — a diverged child count must neither stop mounting early (multi-node item templates inflate it) nor keep the delay-timer path running forever at the end of the list (empty items deflate it). Co-Authored-By: Claude Fable 5 <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.
Summary
Alternative to #26, addressing the same bug plus the issues found while reviewing it. Thanks @girouxc for the diagnosis — the core insight (the mount decision must look at
container.children.length, because items that render empty insert no child node) is adopted here.The bug (#26): when items in a
LazyRow/LazyColumnrender empty,offset()overstates the rendered edge.selected(an index into actual children) then never gets withinbufferofoffset, soupdateOffsetearly-returns forever and navigation stops mounting new items — the list dead-ends.Why not exactly the #26 one-liner: replacing
renderedwithcontainer.children.lengthin both halves of the guard introduces two new problems, becausemaxOffset(props.each.length) is in data units whilechildren.lengthis in node units:children.length. With 20 items × 2 nodes, after 10 items mountchildren.length (20) >= maxOffset (20)— the guard early-returns forever and items 11–20 can never mount.children.lengthnever reachesmaxOffsetwhen any item renders empty, so once everything is mounted the guard never short-circuits; withdelayset, every keypress at the list edge clears and re-armsnavDelayTimeraround a no-op bump, indefinitely.The fix: keep each comparison in its own unit system —
selected < rendered - buffer()) uses child units, matchingselected— this is the [bugfix] Lazy components empty content handling #26 fix.offset() >= maxOffset) stays in data units, immune to the child count diverging in either direction.Also in this PR
tests/lazy.test.tsx) — there was no lazy coverage at all. One test reproduces the [bugfix] Lazy components empty content handling #26 scenario (items rendering empty; fails onmain), one pins the multi-node-template case.VirtualGridbarrel-init fix —VirtualGridcalledlngp.withScrolling(false)at module init through the package barrel, butindex.tsre-exportswithScrollingafterVirtualGrid, so any import with the barrel as entry (including the new tests) threwwithScrolling is not a function. It now imports the module directly.Known remaining inconsistency (out of scope)
lazyScrollToIndexstill mixes units: it setsoffset = index + buffer(data units) butscrollToIndex(index)indexes children. With empty items the target child may not exist — downstream guards (children[index]?.setFocus(),withScrolling's bounds check) prevent a crash, but scroll/focus silently no-op. Fixing it requires mapping child indices back to data indices, which deserves its own change.Testing
npm test— 136/136 passing (11 files), including the two new lazy testsupdateOffsetnpm run tscandnpm run lintclean🤖 Generated with Claude Code