Skip to content

Perf: virtualize the transaction list over 200 rows - #224

Merged
Baskarayelu merged 1 commit into
QuickLendX:mainfrom
greatest0fallt1me:perf/117-virtualize-transaction-list
Aug 28, 2026
Merged

Baskarayelu merged 1 commit into
QuickLendX:mainfrom
greatest0fallt1me:perf/117-virtualize-transaction-list

Conversation

@greatest0fallt1me

@greatest0fallt1me greatest0fallt1me commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Note on scope

No virtualization existed anywhere in the app, and no virtualization library (react-window, react-virtual, ...) is a dependency. Added a small dependency-free hook rather than a new heavy dependency, since this app's lists have a single known fixed row height -- the simple case a ~70-line hook covers without pulling in a general-purpose library. Also opened #116 in parallel, which adds the first real consumer (`TransactionsTable`) this can wire into.

Summary

`useTransactions()` has no cap, and nothing renders it as a windowed list -- a large result set would mount one DOM row per transaction.

Change

`lib/hooks/useVirtualizedList.ts`: `useVirtualizedList(items, { rowHeight, containerHeight, overscan?, threshold? })`.

  • Below `threshold` (default 200) items, it's a no-op -- returns every item, since windowing a handful of rows only adds scroll-math overhead for no benefit (matching "over 200 rows" from the issue title).
  • At/above the threshold, only rows within the visible viewport (+ `overscan` on each side) are returned, with `offsetY`/`totalHeight` for the caller to position them and size the scroll container.
  • The window is clamped against both ends of the list -- an out-of-range `scrollTop` (e.g. stale, or from a resize) can't push the start index past what the list can supply, which would otherwise slice to an empty result.

Tests

`lib/hooks/useVirtualizedList.test.ts`: unvirtualized below threshold, correct window at rest, window/offset shift on scroll, DOM row count stays bounded (`<=20`) even for 50,000 items, and the end-of-list clamp (scrolling far past the end still returns a valid, non-empty window ending at the last item).

`npx vitest run lib/hooks/useVirtualizedList.test.ts`: 5 passed. `npx tsc --noEmit` / `npx eslint` on changed files: clean.

Related to #117

No virtualization existed anywhere, and no virtualization library is
a dependency. Add a small dependency-free useVirtualizedList() for
the single-known-row-height case: below `threshold` (default 200)
items it's a no-op (windowing a handful of rows only adds scroll-math
overhead), at/above it only the rows within the visible viewport (+
overscan) are ever returned, so DOM node count stays roughly constant
regardless of list length. Clamps the window against both ends of the
list so an out-of-range scrollTop can't slice to an empty result.
@Baskarayelu
Baskarayelu merged commit 21eeada into QuickLendX:main Aug 28, 2026
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.

2 participants