From 56945ffd4949e72811bcebb81569cf0cbe35774f Mon Sep 17 00:00:00 2001 From: Molecule AI Core-UIUX Date: Tue, 12 May 2026 04:56:49 +0000 Subject: [PATCH] fix(canvas/mobile): remove ?? [] from Zustand selector to prevent infinite render loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit React error #185 (Maximum update depth exceeded) on mobile chat tab. Root cause: useCanvasStore((s) => s.agentMessages[agentId] ?? []) used a `?? []` fallback in the selector. Zustand uses Object.is for selector equality. When agentMessages[agentId] is undefined (initial state), the fallback creates a NEW [] reference on every store update. Zustand sees this as a state change and re-renders the component. The component reads from the store again, gets another new [] reference, and the cycle repeats until React hits the depth cap. Fix: remove `?? []` from the selector (returns undefined when no messages) and move the fallback to the useState initializer: storedMessages = useCanvasStore(selector) // returns undefined | T[] [messages] = useState(() => (storedMessages ?? []).map(...)) The useState initializer only runs once on mount, so the `?? []` there is safe — it creates the initial state once, then messages are managed via setMessages. Fixes issue #651. Co-Authored-By: Claude Opus 4.7 --- canvas/src/components/mobile/MobileChat.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/canvas/src/components/mobile/MobileChat.tsx b/canvas/src/components/mobile/MobileChat.tsx index f1d290ee..395188fc 100644 --- a/canvas/src/components/mobile/MobileChat.tsx +++ b/canvas/src/components/mobile/MobileChat.tsx @@ -54,9 +54,14 @@ export function MobileChat({ // user sees their prior thread on entry. The store is updated by the // socket → ChatTab flows the desktop runs; on mobile we read from the // same buffer to keep state coherent across viewports. - const storedMessages = useCanvasStore((s) => s.agentMessages[agentId] ?? []); + // NOTE: do NOT use `?? []` in the selector — Zustand uses Object.is + // for selector equality. A fallback `?? []` creates a new [] reference on + // every store update when agentMessages[agentId] is undefined, causing an + // infinite re-render loop (React error #185 / Maximum update depth + // exceeded). The undefined case is handled by the initializer below. + const storedMessages = useCanvasStore((s) => s.agentMessages[agentId]); const [messages, setMessages] = useState(() => - storedMessages.map((m) => ({ + (storedMessages ?? []).map((m) => ({ id: m.id, role: "agent", text: m.content,