[Feature] Lazy-load campaigns via infinite query + virtual scroll (#598)#719
[Feature] Lazy-load campaigns via infinite query + virtual scroll (#598)#719Kaybee973 wants to merge 9 commits into
Conversation
…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
|
@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! 🚀 |
|
Auto-review failed (API error). Leaving PR for human review. |
|
Auto-review failed (API error). Leaving PR for human review. |
|
Auto-review failed (API error). Leaving PR for human review. |
|
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.
|
Auto-review failed (API error). Leaving PR for human review. |
fix: format files with prettier
|
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.
|
Auto-review failed (API error). Leaving PR for human review. |
1 similar comment
|
Auto-review failed (API error). Leaving PR for human review. |
6493e77 to
ce3db8b
Compare
|
Auto-review failed (API error). Leaving PR for human review. |
…roll to Explore page
ce3db8b to
2ee8cfc
Compare
|
Auto-review failed (API error). Leaving PR for human review. |
|
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 |
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 viaget_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 (
visibleCountslice 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
getCampaignsChunk(startIndex, limit)incontractClient.ts— fetches a range of campaigns by ID in parallel, returning{ campaigns, totalCount }useCampaignsrefactored touseInfiniteQuery— 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 datasetCAMPAIGNS_CHUNK_SIZE = 20added tocausesList.ts2. Virtual scrolling
@tanstack/react-virtualinstalleduseWindowVirtualizerreplaces 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)Changes
package.json@tanstack/react-virtualdependencysrc/lib/contractClient.tsgetCampaignsChunk(startIndex, limit)src/lib/causesList.tsCAMPAIGNS_CHUNK_SIZE = 20src/hooks/useCampaigns.tsuseQuerytouseInfiniteQuerywith chunked fetchingsrc/app/[locale]/causes/CausesClient.tsxCampaignVirtualGridusinguseWindowVirtualizer; auto-fetches remaining pages; removesvisibleCount/CAUSES_PAGE_SIZEsrc/__tests__/hooks/useCampaigns.test.tsxgetCampaignsChunk; added pagination controls testsrc/__tests__/integration/CausesFilterUrlSync.test.tsxmatchMediastub; updateduseCampaignsmock with new fieldsTesting
useCampaignsunit tests: loading → success, loading → error, pagination controls, refetch — all passCausesFilterUrlSyncintegration tests: all 3 tests passRelated
Closes #598