fix(canvas/mobile): remove ?? [] from Zustand selector to prevent infinite render loop
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 17s
CI / Detect changes (pull_request) Successful in 54s
E2E API Smoke Test / detect-changes (pull_request) Successful in 49s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 39s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 39s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 18s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 42s
qa-review / approved (pull_request) Failing after 24s
security-review / approved (pull_request) Failing after 23s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
CI / Platform (Go) (pull_request) Successful in 12s
CI / Python Lint & Test (pull_request) Successful in 14s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 12s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 10s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 17s
sop-tier-check / tier-check (pull_request) Successful in 19s
gate-check-v3 / gate-check (pull_request) Successful in 33s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 10m16s
CI / Canvas (Next.js) (pull_request) Successful in 11m57s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (pull_request) Successful in 3s
audit-force-merge / audit (pull_request) Successful in 19s
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 17s
CI / Detect changes (pull_request) Successful in 54s
E2E API Smoke Test / detect-changes (pull_request) Successful in 49s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 39s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 39s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 18s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 42s
qa-review / approved (pull_request) Failing after 24s
security-review / approved (pull_request) Failing after 23s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
CI / Platform (Go) (pull_request) Successful in 12s
CI / Python Lint & Test (pull_request) Successful in 14s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 12s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 10s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 17s
sop-tier-check / tier-check (pull_request) Successful in 19s
gate-check-v3 / gate-check (pull_request) Successful in 33s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 10m16s
CI / Canvas (Next.js) (pull_request) Successful in 11m57s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (pull_request) Successful in 3s
audit-force-merge / audit (pull_request) Successful in 19s
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 <noreply@anthropic.com>
This commit is contained in:
parent
d23bd286ce
commit
56945ffd49
@ -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<ChatMessage[]>(() =>
|
||||
storedMessages.map((m) => ({
|
||||
(storedMessages ?? []).map((m) => ({
|
||||
id: m.id,
|
||||
role: "agent",
|
||||
text: m.content,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user