From 62362084126033d3ec8438b8bc4fe6da2982fc49 Mon Sep 17 00:00:00 2001 From: Ocean82 Date: Tue, 22 Sep 2026 10:13:35 -0500 Subject: [PATCH] Fix infinite re-render loop in NameManagerDialog (React #185) The namedRanges selector returned a fresh [] on every render when the value was undefined, failing Zustand's Object.is check and looping forever. Use a stable module-level constant instead. Also document the same trap on getPinnedMessages. --- src/components/NameManagerDialog.tsx | 7 ++++++- src/store/slices/chatSlice.ts | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/NameManagerDialog.tsx b/src/components/NameManagerDialog.tsx index cc0e235..cee2b4e 100644 --- a/src/components/NameManagerDialog.tsx +++ b/src/components/NameManagerDialog.tsx @@ -9,10 +9,15 @@ import { useFocusTrap } from '@/hooks/useFocusTrap' import { selectionToAbsRange } from '@/lib/namedRanges' import type { NamedRange } from '@/types' +// Stable empty-array reference so the selector below doesn't return a fresh +// `[]` each render (which would fail Zustand's Object.is check and loop +// forever — React error #185). +const EMPTY_NAMED_RANGES: NamedRange[] = [] + export function NameManagerDialog() { const show = useStore((s) => s.showNameManagerDialog) const setShow = useStore((s) => s.setShowNameManagerDialog) - const namedRanges = useStore((s) => s.workbook.namedRanges ?? []) + const namedRanges = useStore((s) => s.workbook.namedRanges ?? EMPTY_NAMED_RANGES) const sheets = useStore((s) => s.workbook.sheets) const selection = useStore((s) => s.selection) const activeSheetId = useStore((s) => s.activeSheetId) diff --git a/src/store/slices/chatSlice.ts b/src/store/slices/chatSlice.ts index 1c3fda5..5973952 100644 --- a/src/store/slices/chatSlice.ts +++ b/src/store/slices/chatSlice.ts @@ -107,6 +107,11 @@ export function createChatActions( if (msg) msg.pinned = !msg.pinned }), + // Allocates a fresh array each call. NEVER call this inside a Zustand + // selector body (e.g. useStore((s) => s.getPinnedMessages())) — the new + // reference fails Object.is every render and causes an infinite re-render + // loop (React error #185). Select the function reference and call it in + // an effect/handler, or wrap the result with useShallow. getPinnedMessages: () => get().messages.filter((m) => m.pinned), sendMessage: () => {