From 6a7ce376a1986ae370a7811d11f8c9f7a9da7180 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 15:30:34 +0000 Subject: [PATCH] perf: Memoize displayList mapping in ProjetsTab to reduce O(N) object allocations on re-renders Co-authored-by: KxlSys <116387953+KxlSys@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ src/pages/matching-page.tsx | 11 ++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 22725eb..76669aa 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -21,3 +21,7 @@ ## 2024-07-10 - O(n) Single Pass Data Aggregation **Learning:** Using chained array methods (e.g. `.map().filter()`, `.flatMap()`, and repeated `.filter()` over the same large list like `profiles`) leads to redundant O(n) passes over data and allocates unnecessary intermediate arrays. This blocks the main thread longer than necessary when calculating basic statistics like `uniqueCities`, `uniqueTechs`, or total counts. **Action:** Consolidate statistical aggregations over large object arrays into a single O(n) `for` loop that updates multiple accumulators (like `Set` objects and primitive counters) in one pass, thereby improving performance and reducing memory overhead. + +## 2024-07-18 - Component Map Memoization Pattern +**Learning:** In React components with highly interactive state (like buttons triggering local state changes such as `visibleCount`), mapping over raw datasets to create derived UI objects outside of a `useMemo` block leads to unnecessary O(N) object allocations and garbage collection on every render. +**Action:** When a component derives lists from props or state for rendering, wrap the mapping or filtering logic inside `useMemo` to cache the result, recalculating only when the underlying data changes, thereby freeing up the main thread from redundant object allocations. diff --git a/src/pages/matching-page.tsx b/src/pages/matching-page.tsx index 263700d..47005db 100644 --- a/src/pages/matching-page.tsx +++ b/src/pages/matching-page.tsx @@ -508,12 +508,17 @@ function ProjetsTab({ } }, [user, interested]); - const displayList: { project: Project; score?: number; reasons?: string[] }[] = - profile && projectMatches.length > 0 + // ⚡ Bolt: Memoize the mapping logic to prevent O(N) object allocations on every render + // This avoids redundant work when local interactive states (like visibleCount or interested) change. + // Impact: Reduces main-thread blocking during re-renders by caching derived datasets. + const displayList: { project: Project; score?: number; reasons?: string[] }[] = useMemo(() => { + return profile && projectMatches.length > 0 ? projectMatches.map((m) => ({ project: m.project, score: m.score, reasons: m.reasons })) : projects.map((p) => ({ project: p })); + }, [profile, projectMatches, projects]); - const visible = displayList.slice(0, visibleCount); + // ⚡ Bolt: Memoize slicing to prevent re-instantiating the visible array on unrelated renders. + const visible = useMemo(() => displayList.slice(0, visibleCount), [displayList, visibleCount]); if (isLoading) { return (