diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..8aa37e4 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,4 @@ + +## 2024-05-30 - [Memoizing List Components in react-window] +**Learning:** Virtualized lists (`react-window`) still re-render internal `Row` components when their parent updates. If the rows are complex or numerous (e.g. `EventList`), rendering them frequently can still cause performance degradation. +**Action:** Use `React.memo` with a custom comparator (e.g. `areEqual` from `react-window`) for rows passed to virtualized lists to stop unnecessary re-renders. Also apply `React.memo` to deeply nested simple row components like `StatListRow` if they are rendered frequently without prop changes. diff --git a/packages/web/src/components/dashboard/event-list.tsx b/packages/web/src/components/dashboard/event-list.tsx index 61a3f68..48ae0f6 100644 --- a/packages/web/src/components/dashboard/event-list.tsx +++ b/packages/web/src/components/dashboard/event-list.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "react"; +import { useMemo, memo } from "react"; import { List, type RowComponentProps } from "react-window"; import { User, Bot, Wrench, ChevronRight } from "lucide-react"; import { @@ -149,7 +149,7 @@ type RowViewProps = { chevron?: "collapsed" | "expanded"; }; -function RowView({ +const RowView = memo(function RowView({ label, preview, time, @@ -199,7 +199,7 @@ function RowView({ ); -} +}); type RowProps = { rows: FlatRow[]; @@ -209,7 +209,19 @@ type RowProps = { onToggleGroup: (firstIdx: number) => void; }; -function Row({ +const areEqual = (prevProps: Readonly>, nextProps: Readonly>) => { + return ( + prevProps.index === nextProps.index && + prevProps.style === nextProps.style && + prevProps.rows === nextProps.rows && + prevProps.selectedIdx === nextProps.selectedIdx && + prevProps.sessionStartedAt === nextProps.sessionStartedAt && + prevProps.onSelect === nextProps.onSelect && + prevProps.onToggleGroup === nextProps.onToggleGroup + ); +}; + +const MemoizedRow = memo(function Row({ index, style, rows, @@ -257,7 +269,7 @@ function Row({ /> ); -} +}, areEqual); export function EventList({ events, @@ -282,7 +294,8 @@ export function EventList({ return ( , string> = { muted: 'bg-muted', } -export function StatListRow({ +export const StatListRow = memo(function StatListRow({ icon, label, value, @@ -52,7 +52,7 @@ export function StatListRow({ ) -} +}) export function StatList({ children,