Repository navigation
Avoid reattaching VirtualizedList cell refs on every render - #59841
Closed
dheerajkoppu wants to merge 2 commits into
Closed
dheerajkoppu wants to merge 2 commits into
dheerajkoppu wants to merge 2 commits into
Conversation
VirtualizedList gave every cell a new ref callback on each render, so React detached and reattached every mounted cell's ref even when the cells bailed out of rendering. Use one stable callback that reads the key from the cell's cellKey prop, and rely on _onCellUnmount to remove entries for unmounted cells.
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:
VirtualizedList._pushCellsgives eachCellRendereran inline ref callback:It's a new function on every render, so on every commit React calls the old callback with
nulland the new one with the instance, for every mounted cell. This happens even whenCellRenderer(aPureComponent) bails out, and the list re-renders often, for example each time the render window moves during a scroll.This replaces it with one
_captureCellRefclass field that finds the slot from the instance'scellKey.cellKeyand the Reactkeyare the same value, so an instance'scellKeynever changes. Thenullcall is ignored: a stable ref is only detached when the cell unmounts or Suspense hides it, and in both casescomponentWillUnmountalready deletes the entry through_onCellUnmount. When Suspense shows the cell again, React calls the ref with the instance and the entry comes back.No behavior change.
Changelog:
[INTERNAL] [CHANGED] - Avoid reattaching VirtualizedList cell refs on every render
Test Plan:
does not reattach cell refs when the list re-renders. It fails onmain, where one re-render of a 5-cell list writes to_cellRefs10 times.removes cell refs when cells unmount.renders all cells after being hidden and revealed by Suspense with a pending batch updatetest covers a cell being hidden and shown again.yarn test packages/virtualized-lists packages/react-native/Libraries/Lists: 11 suites, 206 passed, 1 skipped.yarn flow-checkandyarn lint: no errors in source files.Performance:
Measured in Jest on an M4 Pro (Node 26, React development build). The list re-renders with only
extraDatachanged, so every cell bails out and the time is the list's own render and commit. Each number is the median of 3 runs, and each run is the median of 7 samples of 200 re-renders.Run-to-run ranges don't overlap (main 0.438–0.468 and 2.337–2.507 ms, PR 0.377–0.380 and 1.953–1.969 ms). This is React's development build, so the gain in a release build on a device is likely smaller. I haven't measured that.
Benchmark