From 77523b048dd1f7a3a15984365954ce05f79698f9 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 1 Nov 2026 07:00:51 +0000 Subject: [PATCH] perf(chat): optimize shown token rendering in CodeBlock via useMemo Wrapped the derivation of the `shown` tokens within a `useMemo` block in `CodeBlock.tsx` instead of synchronously recalculating it across nested `.map().join()` calls on every render frame. This prevents unnecessary string computations and array allocations during frequent re-renders on the hot UI path. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ apps/web/src/components/chat/CodeBlock.tsx | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index edb6874221..7b934bec48 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -5,3 +5,7 @@ ## 2026-10-15 - [Performance] Prevent unnecessary UI array recalculations **Learning:** In the chat component (`useThreadList`), derived state that requires filtering and sorting large arrays (like generating nested agent lists) was running synchronously on every render. Given that chat polling triggers frequent re-renders, this caused unnecessary CPU strain on lists that only rarely change. **Action:** Use `useMemo` for derived states like `rows` and `current` in custom hooks when they involve `.filter()`, `.map()`, or `.sort()` and depend on data that updates less frequently than the component renders. + +## 2026-10-18 - [Performance] Optimize CodeBlock Token Concatenation +**Learning:** In the `CodeBlock.tsx` component, we used nested `.map(...).join('')` calls to derive the `shown` token on every render. Just like with the `messagesSignature`, allocating new intermediate arrays and strings dynamically repeatedly creates garbage collection overhead during chat interactions. +**Action:** Utilized standard React memoization (`useMemo`) to wrap the derivation logic. This prevents unnecessary string and array operations on every render without sacrificing code readability. When deriving values on a hot UI path, consider whether React `useMemo` is sufficient before replacing concise functional methods with verbose loops. diff --git a/apps/web/src/components/chat/CodeBlock.tsx b/apps/web/src/components/chat/CodeBlock.tsx index a86bfe9584..72c3a32e5b 100644 --- a/apps/web/src/components/chat/CodeBlock.tsx +++ b/apps/web/src/components/chat/CodeBlock.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'preact/hooks'; +import { useEffect, useRef, useState, useMemo } from 'preact/hooks'; import { Check, Copy } from 'lucide-preact'; import { copyText } from '../../lib/clipboard'; import { highlightCode, normalizeLang, type CodeToken } from '../../lib/highlight'; @@ -26,7 +26,11 @@ export function CodeBlock({ code, lang }: { code: string; lang?: string }) { window.clearTimeout(t); }; }, [code, lang]); - const shown = tokens && tokens.map((l) => l.map((t) => t.content).join('')).join('\n') === code ? tokens : null; + + // Memoize tokens comparison string to avoid recalculating the string flat on every render + const shown = useMemo(() => { + return tokens && tokens.map((l) => l.map((t) => t.content).join('')).join('\n') === code ? tokens : null; + }, [tokens, code]); async function onCopy() { const ok = await copyText(code);