Skip to content

fix(virtual-list): keep the ref handle stable across scroll renders - #151

Merged
RuudBurger merged 1 commit into
mainfrom
ruud/virtuallist-stable-ref
Oct 15, 2026
Merged

RuudBurger merged 1 commit into
mainfrom
ruud/virtuallist-stable-ref

Conversation

@RuudBurger

Copy link
Copy Markdown
Member

VirtualList's useImperativeHandle had no deps, so the handle got rebuilt on every commit, scroll steps included. When React rebuilds a handle it nulls the ref in the mutation phase and only re-attaches it in VirtualList's own layout phase, which runs after the cells' layout effects. So a cell recycled onto a new row in that commit calls getLayout(index) through the parent's ref, gets null, and its seed never gets written. In the Plex app that cell then keeps the previous row's seed (or none), computes the wrong section expansion and writes a bogus 504px reserve, which shifts the focused Home row ~1000px and makes fast Up skip a row (10 9 8 7 6 4 3 2 1, every run).

Fix is one handle for the list's lifetime (useImperativeHandle(ref, ..., [])). The methods read visibleRange, itemAreaOffset, paddingCross, horizontal and the scroll functions through a ref that's updated in a useInsertionEffect. Insertion effects run before any layout effect in the commit, so a cell asking from its layout effect already gets this render's range and offsets (a layout effect would hand it the previous render's). Public API is unchanged.

Also pulled the bare react-reconciler host from the useContentCross spec into createTestRenderer.ts so the new VirtualList spec can share it.

Verified:

  • new spec renders a VirtualList with a cell that reads the ref from its layout effect, scrolls, and checks the handle is there with the new range and layout. Fails on main (ref is null), passes with the fix
  • earlier measurement in the Plex app: swapping the app's ref for a callback ref that ignores null made 12/12 fast-Up runs exact at 700/300/150/80 ms, vs 0/12 without
  • react-lightning-components, react-lightning and react-native-lightning suites, turbo check:types, oxlint

Not run on a real TV or on native platforms.

@RuudBurger
RuudBurger merged commit 213cffb into main Oct 15, 2026
1 check passed
@RuudBurger
RuudBurger deleted the ruud/virtuallist-stable-ref branch October 15, 2026 15:20
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.

1 participant