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.
This commit is contained in:
tao.chen
2026-07-06 18:10:03 +08:00
parent 0122b8bb07
commit fc4bed67f4
+7 -1
View File
@@ -12,6 +12,12 @@ import { FileExplorerPanel } from "@/components/workspace/FileExplorerPanel";
import { StatusBar } from "@/components/workspace/StatusBar"; import { StatusBar } from "@/components/workspace/StatusBar";
import { WorkspaceSelector } from "@/components/workspace/WorkspaceSelector"; import { WorkspaceSelector } from "@/components/workspace/WorkspaceSelector";
import { useWorkspaceUiStore } from "@/lib/store/workspace-ui-store"; 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 { import {
useProcessRestart, useProcessRestart,
useProcessStart, useProcessStart,
@@ -72,7 +78,7 @@ function BottomTabs({ workspaceId }: { workspaceId: string }) {
} }
function TerminalTabs({ 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 activeShellId = useWorkspaceUiStore((s) => s.activeShellIdByWorkspace[workspaceId]);
const setShellsForWorkspace = useWorkspaceUiStore((s) => s.setShellsForWorkspace); const setShellsForWorkspace = useWorkspaceUiStore((s) => s.setShellsForWorkspace);
const addShellToWorkspace = useWorkspaceUiStore((s) => s.addShellToWorkspace); const addShellToWorkspace = useWorkspaceUiStore((s) => s.addShellToWorkspace);