fix(frontend): untrack the splice's own .length read in useMany reconcile

The previous fix wrapped only the comparison reads in `untrack`, but
`handles.splice(0, handles.length, ...next)` still reads `.length`
under the effect's tracking scope — same feedback loop, same
`effect_update_depth_exceeded`.

Move the whole "compare + splice" block inside `untrack`. The
downstream notification on splice still fires (untrack suppresses
dependency subscriptions on the producer side, not write
notifications), so consumers of `handles` still re-render.
This commit is contained in:
Diego Imbert
2026-05-15 15:19:18 +02:00
parent 7ccb2b7941
commit 5b6dc32ee7
+11 -8
View File
@@ -416,14 +416,17 @@ export const UserDraft = {
// produce reference-equal arrays. Avoids dirtying downstream
// reactive readers on no-op `$effect` re-runs.
//
// The read side is wrapped in `untrack` so the effect doesn't
// register `handles` as one of its own dependencies; otherwise
// the splice below would re-fire the effect, splice again, …
// (Svelte raises `effect_update_depth_exceeded`).
const unchanged = untrack(
() => handles.length === next.length && handles.every((h, i) => h === next[i])
)
if (!unchanged) handles.splice(0, handles.length, ...next)
// Both the comparison reads AND the splice's own `.length` read
// run under `untrack` so the effect doesn't register `handles`
// as one of its own dependencies — otherwise the splice would
// re-fire the effect, splice again, … (Svelte raises
// `effect_update_depth_exceeded`). The splice's downstream
// notification still propagates; `untrack` only suppresses the
// dependency subscription on the producer side.
untrack(() => {
const unchanged = handles.length === next.length && handles.every((h, i) => h === next[i])
if (!unchanged) handles.splice(0, handles.length, ...next)
})
}
// Synchronous initial reconcile so single-spec callers (`use()`) get a