Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/stale-keys-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: ensure unowned derived dependencies are not duplicated when reactions are skipped
13 changes: 5 additions & 8 deletions packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -404,15 +404,9 @@ export function update_reaction(reaction) {
skipped_deps = 0;
untracked_writes = null;
active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
// prettier-ignore
skip_reaction =
(flags & UNOWNED) !== 0 &&
(!is_flushing_effect ||
// If we were previously not in a reactive context and we're reading an unowned derived
// that was created inside another reaction, then we don't fully know the real owner and thus
// we need to skip adding any reactions for this unowned
((previous_reaction === null || previous_untracking) &&
/** @type {Derived} */ (reaction).parent !== null));
(!is_flushing_effect || previous_reaction === null || previous_untracking);

derived_sources = null;
set_component_context(reaction.ctx);
Expand Down Expand Up @@ -933,7 +927,10 @@ export function get(signal) {
skipped_deps++;
} else if (new_deps === null) {
new_deps = [signal];
} else {
} else if (!skip_reaction || !new_deps.includes(signal)) {
// Normally we can push duplicated dependencies to `new_deps`, but if we're inside
// an unowned derived because skip_reaction is true, then we need to ensure that
// we don't have duplicates
new_deps.push(signal);
}
}
Expand Down
22 changes: 22 additions & 0 deletions packages/svelte/tests/signals/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,28 @@ describe('signals', () => {
};
});

test('unowned deriveds dependencies are correctly de-duped', () => {
return () => {
let a = state(0);
let b = state(true);
let c = derived(() => $.get(a));
let d = derived(() => ($.get(b) ? 1 : $.get(a) + $.get(c) + $.get(a)));

$.get(d);

assert.equal(d.deps?.length, 1);

$.get(d);

set(a, 1);
set(b, false);

$.get(d);

assert.equal(d.deps?.length, 3);
};
});

test('unowned deriveds correctly update', () => {
return () => {
const arr1 = proxy<{ a: number }[]>([]);
Expand Down