diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..1480ede4 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,6 @@ +## 2024-05-18 - [Recharts Optimization] +**Learning:** In Next.js components utilizing Recharts, transforming props data into chart-ready data points directly in the component body can cause significant performance bottlenecks due to expensive unnecessary recalculations on every React re-render. Some components have `useMemo` optimizations, but others are missing them. +**Action:** Always wrap data transformation logic (e.g., mapping arrays or reducing data arrays) within `useMemo` hooks before passing the result to Recharts components. +## 2024-05-18 - [EventList Rendering Optimization] +**Learning:** In a virtualized list component (e.g., `react-window` List), if a function executed on every row calculates a value based on a parent's constant prop (e.g., parsing a `Date` string), it creates a severe performance bottleneck during scrolling due to redundant string parsing on every render cycle. +**Action:** Always parse constant date strings or pre-calculate expensive parent-level constants using `useMemo` at the top level of the component and pass the computed primitive value (e.g., timestamp in milliseconds) down to the virtualized rows to eliminate repeated processing. diff --git a/packages/web/src/components/dashboard/event-list.tsx b/packages/web/src/components/dashboard/event-list.tsx index a3e4406c..487d31e3 100644 --- a/packages/web/src/components/dashboard/event-list.tsx +++ b/packages/web/src/components/dashboard/event-list.tsx @@ -39,9 +39,9 @@ type FlatRow = const ROW_HEIGHT = 36; -function formatElapsed(timestamp: string, sessionStartedAt: string): string { +function formatElapsed(timestamp: string, sessionStartMs: number): string { const t = new Date(timestamp).getTime(); - const start = new Date(sessionStartedAt).getTime(); + const start = sessionStartMs; if (Number.isNaN(t) || Number.isNaN(start)) return ""; const diffSec = Math.max(0, Math.floor((t - start) / 1000)); const h = Math.floor(diffSec / 3600); @@ -207,7 +207,7 @@ function RowView({ type RowProps = { rows: FlatRow[]; selectedIdx: number; - sessionStartedAt: string; + sessionStartMs: number; onSelect: (idx: number) => void; onToggleGroup: (firstIdx: number) => void; }; @@ -217,7 +217,7 @@ function Row({ style, rows, selectedIdx, - sessionStartedAt, + sessionStartMs, onSelect, onToggleGroup, }: RowComponentProps) { @@ -230,7 +230,7 @@ function Row({ onToggleGroup(row.groupFirstIdx)} @@ -252,7 +252,7 @@ function Row({ onSelect(row.idx)} @@ -271,6 +271,8 @@ export function EventList({ expandedGroups, onToggleGroup, }: EventListProps) { + const sessionStartMs = useMemo(() => new Date(sessionStartedAt).getTime(), [sessionStartedAt]); + const rows = useMemo( () => buildFlatRows(groups, expandedGroups, selectedIdx), [groups, expandedGroups, selectedIdx], @@ -292,7 +294,7 @@ export function EventList({ rowProps={{ rows, selectedIdx, - sessionStartedAt, + sessionStartMs, onSelect, onToggleGroup, }}