Skip to content

[Feature] Lazy-load campaigns via infinite query + virtual scroll (#598)#719

Open
Kaybee973 wants to merge 9 commits into
Iris-IV:mainfrom
Kaybee973:feat/issue-598-virtual-scroll-infinite-query
Open

[Feature] Lazy-load campaigns via infinite query + virtual scroll (#598)#719
Kaybee973 wants to merge 9 commits into
Iris-IV:mainfrom
Kaybee973:feat/issue-598-virtual-scroll-infinite-query

Conversation

@Kaybee973

Copy link
Copy Markdown

Summary

Fixes #598 by replacing the monolithic campaign fetch + client-side slice pagination with chunked infinite-query fetching and a DOM-virtualized grid powered by @tanstack/react-virtual. This dramatically reduces initial load time and keeps the DOM lean when browsing 100+ campaigns.

Problem

The causes page called getAllCampaigns() which sequentially fetched every campaign from the Soroban contract via get_campaign(id) before rendering anything. With 100+ campaigns that means 101+ contract calls on page load. All campaigns were rendered as DOM nodes, degrading scroll performance proportionally to the campaign count.

Client-side pagination (visibleCount slice with a "Load More" button) reduced what the user saw but not what the browser had to manage in memory.

Solution

Two independent but complementary changes:

1. Chunked infinite-query data loading

  • New getCampaignsChunk(startIndex, limit) in contractClient.ts — fetches a range of campaigns by ID in parallel, returning { campaigns, totalCount }
  • useCampaigns refactored to useInfiniteQuery — loads the first chunk immediately (20 campaigns), then auto-fetches remaining chunks in the background so filtering, sorting, and vote-count loading work on the complete dataset
  • CAMPAIGNS_CHUNK_SIZE = 20 added to causesList.ts

2. Virtual scrolling

  • @tanstack/react-virtual installed
  • useWindowVirtualizer replaces the CSS grid's .map() with a row-based virtualizer that only renders the 2-3 rows visible in the viewport (plus overscan)
  • useColumns() breakpoint hook groups campaigns into rows matching the current CSS grid column count (1/2/3)
  • The old "Load More" button is replaced with a subtle spinner that appears while remaining chunks are still loading

Changes

File Change
package.json Added @tanstack/react-virtual dependency
src/lib/contractClient.ts Added getCampaignsChunk(startIndex, limit)
src/lib/causesList.ts Added CAMPAIGNS_CHUNK_SIZE = 20
src/hooks/useCampaigns.ts Refactored from useQuery to useInfiniteQuery with chunked fetching
src/app/[locale]/causes/CausesClient.tsx Replaced "Load More" grid with CampaignVirtualGrid using useWindowVirtualizer; auto-fetches remaining pages; removes visibleCount/CAUSES_PAGE_SIZE
src/__tests__/hooks/useCampaigns.test.tsx Updated to mock getCampaignsChunk; added pagination controls test
src/__tests__/integration/CausesFilterUrlSync.test.tsx Added matchMedia stub; updated useCampaigns mock with new fields

Testing

  • useCampaigns unit tests: loading → success, loading → error, pagination controls, refetch — all pass
  • CausesFilterUrlSync integration tests: all 3 tests pass
  • ✅ TypeScript type-check: no errors
  • ✅ ESLint: 0 errors (pre-existing warnings only)

Related

Closes #598

…V#598)

- Install @tanstack/react-virtual for DOM-efficient grid rendering
- Add getCampaignsChunk(startIndex, limit) to contractClient for
  chunked campaign fetching
- Refactor useCampaigns from useQuery to useInfiniteQuery, loading
  campaigns in chunks (20 per page)
- Auto-fetch remaining chunks in background so filter/sort work on
  the full dataset
- Replace the client-side slice + 'Load More' button in CausesClient
  with a window-virtualized grid (useWindowVirtualizer)
- Add useColumns() breakpoint hook for responsive row grouping
- Add CAMPAIGNS_CHUNK_SIZE constant alongside existing CAUSES_PAGE_SIZE
- Update useCampaigns tests for the new paginated contract API
- Add matchMedia stub to CausesFilterUrlSync integration test
@drips-wave

drips-wave Bot commented Jun 30, 2026

Copy link
Copy Markdown

@Kaybee973 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@sshdopey

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@sshdopey

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@sshdopey

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@sshdopey

sshdopey commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

Adds the useColumns() hook that was being called at line 124 but never defined.
The hook returns the responsive column count (1, 2, or 3) based on viewport width:
- 1 column on mobile (<768px)
- 2 columns on tablet (768px-1024px)  
- 3 columns on desktop (≥1024px)

Listens for window resize events to update dynamically.
@sshdopey

sshdopey commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@sshdopey

sshdopey commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

Adds the useColumns() hook that was being called at line 124 but never defined.
The hook returns the responsive column count (1, 2, or 3) based on viewport width:
- 1 column on mobile (<768px)
- 2 columns on tablet (768px-1024px)  
- 3 columns on desktop (≥1024px)

Listens for window resize events to update dynamically.
@sshdopey

sshdopey commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

1 similar comment
@sshdopey

sshdopey commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@Kaybee973 Kaybee973 force-pushed the feat/issue-598-virtual-scroll-infinite-query branch from 6493e77 to ce3db8b Compare July 3, 2026 09:54
@sshdopey

sshdopey commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@Kaybee973 Kaybee973 force-pushed the feat/issue-598-virtual-scroll-infinite-query branch from ce3db8b to 2ee8cfc Compare July 3, 2026 10:04
@sshdopey

sshdopey commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Auto-review failed (API error). Leaving PR for human review.

@davidmaronio

Copy link
Copy Markdown
Contributor

the infinite-query + virtual-scroll feature looks coherent. two ci fixes: (1) Validate commit messages fails, the commit "Fix: Add missing useColumns hook..." violates the conventional format (capital "Fix"), reword to fix: add missing useColumns hook, and (2) it's dirty, rebase onto main. the Playwright red is our shared broken e2e, ignore it.

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.

[Feature] Add lazy loading and virtual scrolling to campaign list for 100+ campaigns

3 participants