diff --git a/.jules/bolt.md b/.jules/bolt.md index 231af188b..f61d2f720 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -10,3 +10,6 @@ ## 2024-05-24 - Memoizing inline array maps **Learning:** Inline mapping of arrays inside JSX in large React components causes O(N) recalculation on every render. **Action:** Wrap inline JSX elements that map over arrays (e.g., lists of tasks) in a `useMemo` hook with specific dependencies. +## 2025-02-12 - Wrap email list array mapping in useMemo +**Learning:** In React components like `EmailList`, when long arrays (like `emails`) are mapped to JSX components directly inside the return block, any irrelevant state change (like a single keystroke updating `searchQuery`) forces a complete recalculation and re-render of all list items, blocking the main thread. +**Action:** Extract large array mapping logic into a `useMemo` block that only recalculates when its specific dependencies (e.g., `emails`, selection state) change, effectively preventing O(N) recalculations on unrelated state updates like user input. diff --git a/frontend/src/components/EmailList.tsx b/frontend/src/components/EmailList.tsx index 6dbb722ea..3a3e11a26 100644 --- a/frontend/src/components/EmailList.tsx +++ b/frontend/src/components/EmailList.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState, memo } from 'react'; +import React, { useCallback, useEffect, useRef, useState, memo, useMemo } from 'react'; import { apiClient } from '@/lib/api-client'; import { ScrollArea } from "@/components/ui/scroll-area"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; @@ -171,6 +171,33 @@ export function EmailList({ emptyTitle: '맥락 검색 결과가 없습니다', emptyBody: '맥락 검색어를 바꾸거나 메일 동기화 상태를 확인하세요.', }; + // ⚡ Bolt: Wrap email list in useMemo to prevent O(N) re-renders when search input changes + // 🎯 Why: Mapping over long lists of emails blocks the main thread during unrelated state updates like typing in the search box. + const emailListContent = useMemo(() => { + if (loading) { + return
메일을 불러오는 중입니다...
; + } + if (error) { + return
{error}
; + } + if (emails.length === 0) { + return ( +
+

{folderCopy.emptyTitle}

+

{folderCopy.emptyBody}

+
+ ); + } + return emails.map((email: EmailItem) => ( + + )); + }, [loading, error, emails, folderCopy.emptyTitle, folderCopy.emptyBody, selectedEmailId, onSelectEmail]); + const searchBusy = isSearching || loading; return ( @@ -249,25 +276,7 @@ export function EmailList({
- {loading ? ( -
메일을 불러오는 중입니다...
- ) : error ? ( -
{error}
- ) : emails.length === 0 ? ( -
-

{folderCopy.emptyTitle}

-

{folderCopy.emptyBody}

-
- ) : ( - emails.map((email: EmailItem) => ( - - )) - )} + {emailListContent}