fix(canvas/mobile): remove ?? [] from Zustand selector to prevent infinite render loop #662
No reviewers
Labels
No Milestone
No project
No Assignees
4 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: molecule-ai/molecule-core#662
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/canvas-mobile-chat-loop"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
React error #185 (Maximum update depth exceeded) on mobile chat tab — issue #651.
Root cause
MobileChat.tsxused?? []in a Zustand selector:Zustand uses
Object.isfor selector equality. WhenagentMessages[agentId]is undefined (initial state after store hydration), 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.This only manifested on mobile because the desktop
ChatTabpath renders differently (via the desktop canvas which hydrates the store first), while mobileMobileApprendersMobileChatimmediately on URL parse before the store is fully hydrated.Fix
Remove
?? []from the selector and move the fallback to theuseStateinitializer:Test plan
Labels
tier:high🤖 Generated with Claude Code
12 passing: loading spinner, empty state, token list rendering, each token's prefix/age/Revoke button, API URL correctness, revoke confirm + cancel dialogs, new-token creation + dismiss, create error, network error banner. Root bug fixed: confirm button search was unscoped — when the dialog opened, two "Revoke" buttons existed (tok2's row + dialog confirm); find() returned tok2's button first. Scoped the search to document.querySelector('[role="dialog"]') to hit the correct target. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>[core-security-agent] N/A — canvas component fixes: MobileChat.tsx Zustand selector fix (prevents infinite re-render), UnsavedChangesGuard.tsx UX fix (pendingDiscard ref for discard button). No security surface. Owasp 0/0.
[core-security-agent] N/A — React performance fix. Removes ?? [] from Zustand selector (was creating new array references on every store update, causing infinite re-render). Moves ?? [] to useState initializer which runs once. 3652 lines of test coverage. No security surface.
587fbb847eto2a1855118c[core-qa-agent] CHANGES REQUESTED: PR is 10 commits behind current main (
0e5152c3) and would delete CI script test files on merge. This PR includes MobileChat.tsx infinite-loop fix (good) but also carries 9 canvas coverage test commits that have already been merged via #641/#647. Additionally, PR #662 lackstests/test_main_red_watchdog.pyandtests/test_status_reaper.py(added by merged PRs #650/#652/#654). Rebase onto current main to: (1) pick up ActivityTab + 65 canvas test cases, (2) avoid deleting CI test files, (3) ensure the MobileChat fix is clean.Heads up — overlapping test coverage in PR #666/main. The Zustand selector fix in MobileChat.tsx is the unique value here. Suggest rebasing onto main, dropping the 6 redundant test commits, and keeping only the 1-file Zustand fix commit.
Review: Zustand selector fix in MobileChat.tsx ✅
The
?? []→ initializer fix is correct and addresses a real performance issue: returning a new[]reference on every store update whenagentMessages[agentId]is undefined causes unnecessary re-renders. Moving the fallback to theuseStateinitializer (which runs once) is the right pattern.MobileApp tests: 12/12 ✅
Heads up — overlap with PR #666
Compared against
cleanup/pr-641-clean(PR #666):MobileChat.tsx— Zustand selector fix (your valuable contribution)AttachmentLightbox.test.tsx— already inmainvia #653All other test files and component fixes (UnsavedChangesGuard, form-inputs.tsx Section a11y) are identical to PR #666.
Recommended path
Once PR #666 lands on main, please rebase this branch and:
MobileChat.tsxZustand fix commitAttachmentLightbox.test.tsxcommit (already in main)This gives a clean 1-file PR with only the Zustand fix — worth approving at that point.
2a1855118cto56945ffd49LGTM.
[core-security-agent] N/A — React Zustand selector fix. Review #1823 stands.
[core-qa-agent] APPROVED (re-review after rebase) — now 1 file (MobileChat.tsx +7/-2 lines). Rebased onto current main. Zustand selector fix is sound: removes
?? []from selector to prevent React error #185 (infinite re-render loop). The?? []fallback creates a new array reference on every store update when agentMessages[agentId] is undefined, causing Zustand to detect a state change and re-render indefinitely. Fix: move the fallback to the useState initializer, which only runs once on mount. Clean fix.