diff --git a/.jules/bolt.md b/.jules/bolt.md index 97d21a9e6..fa2deda3f 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -23,3 +23,6 @@ **Learning:** When generating derived UI state in `useMemo` that joins separate data arrays (like graph edges referencing node IDs), calling helper functions that use `Array.prototype.find()` for every item creates an `O(M * N)` bottleneck. **Action:** When a loop needs to repeatedly look up related items from another array by ID, pre-compute an `O(N)` `Map` before the loop and use `map.get()` for `O(1)` lookups instead of inline array `.find()` calls. +## 2024-05-24 - [React Component Memoization] +**Learning:** In React components like `WorkspaceHome`, when layout state or polling changes trigger parent re-renders, expensive child components like `EmailDetail` will also re-render unnecessarily if not memoized. +**Action:** Always consider `React.memo` for heavy child components that rely on stable props (like IDs) when the parent component has frequent unrelated state updates. diff --git a/frontend/src/components/EmailDetail.tsx b/frontend/src/components/EmailDetail.tsx index 35263d783..5a6767085 100644 --- a/frontend/src/components/EmailDetail.tsx +++ b/frontend/src/components/EmailDetail.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState, memo } from 'react'; import { apiClient } from '@/lib/api-client'; import { Separator } from "@/components/ui/separator"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; @@ -102,7 +102,10 @@ function normalizeLlmData(payload: unknown): LlmData { }; } -export function EmailDetail({ emailId, actionCommand = null }: { emailId: number | null; actionCommand?: EmailDetailActionCommand | null }) { +// ⚡ Bolt: Memoized EmailDetail to prevent unnecessary re-renders +// 🎯 Why: Re-renders of EmailDetail when the parent components (like WorkspaceHome) re-render can cause performance issues, especially when switching active layout tabs or receiving polling updates that don't affect the selected email. +// 📊 Impact: Significantly reduces React reconciliation work when the workspace state changes but the selected email remains the same. +export const EmailDetail = memo(function EmailDetail({ emailId, actionCommand = null }: { emailId: number | null; actionCommand?: EmailDetailActionCommand | null }) { const [email, setEmail] = useState(null); const [threadEmails, setThreadEmails] = useState([]); const [llmData, setLlmData] = useState(null); @@ -883,4 +886,4 @@ export function EmailDetail({ emailId, actionCommand = null }: { emailId: number /> ); -} +});