From 5b6dc32ee725a4a5a279c79d2e4403634bb4064f Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Fri, 15 May 2026 15:19:00 +0200 Subject: [PATCH] fix(frontend): untrack the splice's own .length read in useMany reconcile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- frontend/src/lib/userDraft.svelte.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/userDraft.svelte.ts b/frontend/src/lib/userDraft.svelte.ts index 11c74f36f9..4a4420a122 100644 --- a/frontend/src/lib/userDraft.svelte.ts +++ b/frontend/src/lib/userDraft.svelte.ts @@ -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