fix(perf): virtualize watchlist table rows#4427
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
…nder Addresses the P1 + P2 review findings on the inline watchlist virtualization without changing the data contracts or adopting a new primitive. - P1: scroll now updates the visible window IN PLACE (swap only the <tbody> via setTrustedHtml) instead of calling onRerender(). The owning panels route rerenders through Panel.setSafeContent, which trailing-debounces the innerHTML swap 150ms; a fling never let that settle, so the window froze (blank rows) while listeners piled up on the un-swapped DOM. Row clicks are now delegated on the stable <table> so they survive the in-place tbody swaps. - rAF-gate the scroll handler so at most one window update runs per frame. - Clamp the scroll->index mapping to maxStart (no more redundant rerenders at the bottom) and subtract the expanded-detail height when the expanded row is above the window (no more blank band). - Pin each data row to the virtual row height via a --watchlist-row-height custom property (single-line, no wrap) so the spacer + scroll math can't drift with cell content (notably wrapped names on mobile); cross-referenced with the VIRTUAL_ROW_HEIGHT_PX constant. - Make render() pure: compute the clamped start once and thread it through renderTableBody / getRenderedRowCount (no state mutation, no ordering trap). - Memoize the filtered+sorted list (keyed on items/sort/filter/search) so a pure window shift no longer re-sorts the whole list. - Tests: replace the source-text "no resort" assertion with a behavioral sort-count test; add coverage for maxStart clamping, expanded-detail offset, pure-render clamp-on-shrink, below-window spacer, and the 100/101 threshold. Verified: tsc --noEmit, the focused test (10/10), biome lint, safe-html and boundary lints all pass. The full `vite build` could not run in this worktree (the shared node_modules is missing the declared @fontsource/nunito dep — same failure on a clean checkout, unrelated to this change); CI runs the real build. Live scroll/fling behavior warrants a browser QA pass. Deferred: review finding #2 (reuse the repo's VirtualList/WindowedList primitive) is a larger refactor left as a follow-up. Claude-Session: https://claude.ai/code/session_01SfU43TAhhi8VSE5geCdCjT
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
Intent
Fix #3997 by removing the large eager
<tbody>render path fromWatchlistTableViewwithout changing the owning panels' data contracts or user-facing table controls.Non-goals: changing premium data fetching, changing watchlist defaults, or introducing a new virtualization dependency.
Validation Matrix
.watchlist-rowentries and ~42 KB of table HTML.TMPDIR=/tmp ./node_modules/.bin/tsx --test tests/watchlist-table-view-virtualization.test.mtsoutside sandbox, 5 tests passed.npm run typecheck.page.setContent()probe reported 618 total source rows, 44 mounted.watchlist-rows, 152 total DOM elements, and 1 spacer.VITE_VARIANT=full ./node_modules/.bin/vite build.Sandbox note: sandboxed
tsxfailed before tests withlisten EPERMon its IPC pipe, even withTMPDIR=/tmp; the exact focused command was rerun outside the sandbox and passed.Review Gates
022fa8944992b8f1e3ecc25f98a3d2489112da17.Documentation
Not applicable. This is an internal rendering/performance fix with focused tests.
Screenshots / UI Evidence
Not applicable for a non-visual table virtualization change. Runtime DOM property evidence is included in the validation matrix.
Residual Findings
None.