From 63d0fa8cbe75d4a3752232850e717325f6de34a0 Mon Sep 17 00:00:00 2001
From: iotserver24 <147928812+iotserver24@users.noreply.github.com>
Date: Tue, 5 May 2026 18:57:41 +0000
Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Use=20useMemo=20for=20mappi?=
=?UTF-8?q?ng=20messages=20array=20to=20prevent=20O(N)=20VDOM=20recreation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Extracted the `messages.map` logic in `ChatPanel` into a `useMemo` block. This prevents the entire array from being re-mapped four times a second when `App.tsx` sends the `runElapsed` prop updates.
---
.jules/bolt.md | 4 +++
.../src/renderer/components/ChatPanel.tsx | 33 +++++++++++--------
2 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/.jules/bolt.md b/.jules/bolt.md
index 678e65a..afcab34 100644
--- a/.jules/bolt.md
+++ b/.jules/bolt.md
@@ -35,3 +35,7 @@
## 2024-05-04 - Unnecessary Timer Re-renders
**Learning:** App.tsx has a 250ms setInterval updating state (runElapsed) while the agent is running, which forces the entire app tree (including large lists in FileExplorer and ChatPanel) to re-render 4 times a second.
**Action:** Always wrap large list components and recursive tree components (like FileExplorer's Row) in React.memo() when they are descendants of a component with high-frequency state updates. Also use useMemo for mapping large lists (like chat messages) to prevent unnecessary VDOM recreation.
+
+## 2024-05-18 - [O(N) VDOM Recreation in ChatPanel with React]
+**Learning:** Even if list item components (like `MessageBubble` and `ToolCallCard`) are memoized using `React.memo()`, mapping over a large array of messages inside the render cycle of a component that receives high-frequency prop updates (like `runElapsed` from a 250ms `setInterval`) still creates O(N) evaluations and recreates VDOM elements. This adds significant garbage collection overhead and reduces responsiveness.
+**Action:** Always wrap the array mapping logic (`messages.map(...)`) itself with `useMemo` in parent components to completely prevent the O(N) iteration and VDOM element recreation on unrelated state changes.
diff --git a/packages/desktop/src/renderer/components/ChatPanel.tsx b/packages/desktop/src/renderer/components/ChatPanel.tsx
index 9b7e03e..88640ee 100644
--- a/packages/desktop/src/renderer/components/ChatPanel.tsx
+++ b/packages/desktop/src/renderer/components/ChatPanel.tsx
@@ -1,4 +1,4 @@
-import { useState, useRef, useEffect } from 'react';
+import { useState, useRef, useEffect, useMemo } from 'react';
import { Send, Terminal, Zap, BookOpen } from 'lucide-react';
import type { ChatMessage } from '../App';
import type { ModeState } from '../../preload/index';
@@ -79,6 +79,23 @@ export default function ChatPanel({
if (inputRef.current) inputRef.current.style.height = 'auto';
};
+ // ⚡ Bolt: Memoize the mapping of messages to prevent O(N) VDOM recreation on frequent timer ticks (e.g. runElapsed)
+ const renderedMessages = useMemo(() => {
+ return messages.map((msg) =>
+ msg.role === 'tool' && msg.toolName ? (
+