Skip to content

fix(deploy): resolve infinite re-render in DeployControl - #41

Closed
jidechao wants to merge 1 commit into
nexu-io:mainfrom
jidechao:fix/deploy-control-infinite-loop
Closed

fix(deploy): resolve infinite re-render in DeployControl#41
jidechao wants to merge 1 commit into
nexu-io:mainfrom
jidechao:fix/deploy-control-infinite-loop

Conversation

@jidechao

Copy link
Copy Markdown

Summary

  • Fix "Maximum update depth exceeded" crash caused by unstable [] fallback in the deployments Zustand selector in DeployControl
  • Replace with a module-level stable empty array constant (EMPTY_DEPLOYMENTS), matching the existing pattern used for EMPTY_LOG and EMPTY_STATS in preview-pane.tsx

Root Cause

useStore((s) => selectActiveTask(s)?.deployments ?? []) creates a new [] reference on every render. Zustand detects the reference change, re-renders the component, which creates another new [], causing an infinite loop.

Test plan

  • Run pnpm dev and open the app
  • Verify no "getSnapshot should be cached" or "Maximum update depth exceeded" errors in the browser console
  • Verify DeployControl still renders correctly after a Convert completes
  • Verify deployment history dropdown still works

The `?? []` fallback in the deployments selector created a new array
reference on every render, causing Zustand to detect a state change and
trigger an infinite update loop ("Maximum update depth exceeded").

Replace with a module-level stable empty array constant, matching the
pattern already used for EMPTY_LOG and EMPTY_STATS in preview-pane.tsx.
@lefarcen
lefarcen requested a review from mrcfps May 16, 2026 03:13
@lefarcen lefarcen added size/XS Extra-small change (<20 lines) risk/medium Medium risk change type/bugfix Bug fix labels May 16, 2026
@lefarcen

Copy link
Copy Markdown

Heads-up: PR #33 is already open against the same DeployControl selector loop. Both PRs touch src/components/deploy-control.tsx and replace the freshly allocated deployments ?? [] fallback with a shared empty deployments array.

You and @cszhouwei may want to compare approaches; the maintainer team will decide which one lands. Sharing this so neither effort gets wasted.

@mrcfps mrcfps left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jidechao Reviewed the DeployControl selector fix and the new EMPTY_DEPLOYMENTS fallback; this matches the stable selector pattern already used for EMPTY_LOG and EMPTY_STATS, so the changed range looks correct for stopping the render loop when a task has no deployments. Thanks for jumping on this bugfix so quickly 🙌

🔁 Powered by Looper · runner=reviewer · agent=opencode · An autonomous AI dev team for your GitHub repos.

@lefarcen

Copy link
Copy Markdown

Adding one more coordination note: PR #42 is now also open against this same DeployControl selector loop. It touches src/components/deploy-control.tsx and uses the same stable empty deployments fallback direction as #33 and this PR.

The maintainer team will choose which focused fix lands; noting the overlap so everyone has the latest picture.

joeylee12629-star pushed a commit that referenced this pull request May 16, 2026
Replace inline `?? []` fallback with a module-scope readonly empty array so the zustand selector returns a stable reference, fixing the React 19 / Next.js 16 `getSnapshot should be cached to avoid an infinite loop` console error in DeployControl.

Supersedes #25, #41, #42.
@joeylee12629-star

Copy link
Copy Markdown
Contributor

Superseded by #33 (squash-merged). Same fix landed via @cszhouwei's PR with the cleanest implementation (readonly module-scope constant). Thanks for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk/medium Medium risk change size/XS Extra-small change (<20 lines) type/bugfix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants