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 (