Skip to content

bugfix(core): prevent infinite render loop for array/object control v…#129

Merged
dallegos merged 1 commit into
mainfrom
bugfix/multiselect-render-loop
Jun 22, 2026
Merged

bugfix(core): prevent infinite render loop for array/object control v…#129
dallegos merged 1 commit into
mainfrom
bugfix/multiselect-render-loop

Conversation

@dallegos

Copy link
Copy Markdown
Contributor

📋 Description

What does this PR do?

Fixes an infinite render loop (Maximum update depth exceeded) that appeared in v2.5 for any control whose value is an array or object — in practice, the multiselect control.

Root cause (two pieces):

  1. hasControlsChanged compared a control's value by reference (!==). A control with an array/object value (like multiselect, whose value consumers typically rebuild each render via .map(...)) was reported as changed on every render, so registerSection fired every render.
  2. v2.5 swapped the store's setState comparison from JSON.stringify to deepEqual (commit b5f762b). deepEqual compares functions by reference, and the hook re-wraps onChange of persist controls into new functions on every render — so each redundant re-registration became a real store mutation. Since the registration effect depends on sections, the store update re-triggered the effect → infinite loop.

Before v2.5 this never closed the loop because JSON.stringify dropped functions and compared array content, making redundant re-registrations no-ops.

Fix: compare control value structurally with deepEqual (already imported), and deep-compare multiselect options as well. Adds regression tests and a patch changeset.


🎯 Type of change

  • 🐛 Bugfix - Fixes an existing bug

🏗️ Affected areas

  • 🪝 Hook / Core (src/hooks, src/managers) - useDevPanel, auto-mount, manager
  • 🧪 Tests (*.spec.ts)

🔗 Related links

Release

  • Includes a Changeset (npm run changeset) for consumer-facing changes

⚠️ Notes for reviewers

Key points to review

  • The single behavioral change is in hasControlsChanged: value comparison moved from !== to deepEqual. This is also more correct generally — multiselect options previously had no comparison branch, so option changes went undetected.
  • Among current control types, multiselect is the only one with an array/object value; all others (boolean, string, number) are primitives. The fix is generic, so future controls with array/object values are covered automatically.

Changes requiring special attention

  • The deeper interaction with BaseStoreService.setState's deepEqual (which detects handler-only changes) is left as-is; this PR breaks the loop at the hasControlsChanged layer. No change to store semantics.

Additional setup required

  • None.

…alues

hasControlsChanged compared a control's `value` by reference (`!==`), so any
control whose value is an array or object (e.g. multiselect) was reported as
changed on every render, re-registering the section each time. Since v2.5 the
store's setState uses deepEqual (which no longer drops onChange handlers), each
redundant re-registration became a real store mutation, and the registration
effect depends on `sections` — closing the loop into "Maximum update depth
exceeded".

Compare control values structurally with deepEqual instead, and also deep-compare
multiselect `options`. Adds regression tests for multiselect.
@dallegos dallegos requested a review from a team as a code owner June 22, 2026 11:40
@dallegos dallegos added the bug Something isn't working label Jun 22, 2026
@changeset-bot

changeset-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: afc55bc

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@berenjena/react-dev-panel Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dallegos dallegos merged commit 3edbd8c into main Jun 22, 2026
3 checks passed
@dallegos dallegos deleted the bugfix/multiselect-render-loop branch June 22, 2026 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant