From fc4bed67f4b5341d8def70a52266781eb309ef8e Mon Sep 17 00:00:00 2001 From: "tao.chen" <93983997+taochen-ct@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:10:03 +0800 Subject: [PATCH] fix(web): cache empty shells array in TerminalTabs selector useWorkspaceUiStore((s) => s.shellsByWorkspace[workspaceId] ?? []) returned a fresh [] on every render when the workspace had no shells yet, so useSyncExternalStore saw a new snapshot each render and re-rendered, which ran the selector again, ad infinitum. "The result of getSnapshot should be cached to avoid an infinite loop" TerminalTabs @ WorkspaceShell.tsx:75 Fix: hoist a module-level EMPTY_SHELLS constant. The selector now returns a stable reference for the empty case. The non-empty case already returns the array stored in the Zustand store, whose reference is set once per setShellsForWorkspace call. --- web/src/components/layout/WorkspaceShell.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/components/layout/WorkspaceShell.tsx b/web/src/components/layout/WorkspaceShell.tsx index 9ee3bff..0b1174c 100644 --- a/web/src/components/layout/WorkspaceShell.tsx +++ b/web/src/components/layout/WorkspaceShell.tsx @@ -12,6 +12,12 @@ import { FileExplorerPanel } from "@/components/workspace/FileExplorerPanel"; import { StatusBar } from "@/components/workspace/StatusBar"; import { WorkspaceSelector } from "@/components/workspace/WorkspaceSelector"; import { useWorkspaceUiStore } from "@/lib/store/workspace-ui-store"; + +// Module-level constant so the selector returns a stable reference when +// the workspace has no shells yet. Returning a freshly-allocated `[]` from +// the selector (via `?? []`) would make useSyncExternalStore see a new +// snapshot on every render and loop. +const EMPTY_SHELLS: string[] = []; import { useProcessRestart, useProcessStart, @@ -72,7 +78,7 @@ function BottomTabs({ workspaceId }: { workspaceId: string }) { } function TerminalTabs({ workspaceId }: { workspaceId: string }) { - const shells = useWorkspaceUiStore((s) => s.shellsByWorkspace[workspaceId] ?? []); + const shells = useWorkspaceUiStore((s) => s.shellsByWorkspace[workspaceId] ?? EMPTY_SHELLS); const activeShellId = useWorkspaceUiStore((s) => s.activeShellIdByWorkspace[workspaceId]); const setShellsForWorkspace = useWorkspaceUiStore((s) => s.setShellsForWorkspace); const addShellToWorkspace = useWorkspaceUiStore((s) => s.addShellToWorkspace);