fix: resolve effect_update_depth_exceeded with select bind:value in legacy mode #17645
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #13768
<select bind:value={derived.prop}>in legacy (non-runes) components throwseffect_update_depth_exceededwhen the bound value comes from a$:reactive statement.Root cause:
setup_select_synchronizationcreated atemplate_effectthat calledinvalidate_inner_signals, which reads and writes the same signals on every change — creating an infinite update loop when those signals feed back into derived state.Fix: Remove the effect-based synchronization entirely. Instead, populate
legacy_indirect_bindingsduring the analyze phase for<select bind:value>elements, and callinvalidate_inner_signalsinline at the mutation point inAssignmentExpression— only when the binding is actually mutated, avoiding the read-write cycle.Based on the approach outlined in #16200.
Changes
scope.js: Addlegacy_indirect_bindingsfield toBindingclassRegularElement.js(analyze): For<select bind:value={foo}>, collect scope references as indirect bindings on the bound variableRegularElement.js(transform): Removesetup_select_synchronizationfunction and its call siteAssignmentExpression.js(transform): When mutating a binding with indirect bindings, appendinvalidate_inner_signalscall after the mutationTest plan
binding-select-reactive-derivedtest that reproduces the exact scenario from Broken page with Svelte 5: uncaughteffect_update_depth_exceeded#13768