diff --git a/.changeset/rotten-actors-rush.md b/.changeset/rotten-actors-rush.md new file mode 100644 index 000000000000..46f15bdf2738 --- /dev/null +++ b/.changeset/rotten-actors-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure unowned derived signals correctly re-connect to graph diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index bd4e387c3db9..dc103bbea624 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -196,18 +196,20 @@ export function check_dirtiness(reaction) { update_derived(/** @type {Derived} */ (dependency)); } - if (dependency.version > reaction.version) { - return true; + // If we are working with an unowned signal as part of an effect (due to !current_skip_reaction) + // and the version hasn't changed, we still need to check that this reaction + // is linked to the dependency source – otherwise future updates will not be caught. + if ( + is_unowned && + current_effect !== null && + !current_skip_reaction && + !dependency?.reactions?.includes(reaction) + ) { + (dependency.reactions ??= []).push(reaction); } - if (is_unowned) { - // TODO is there a more logical place to do this work? - if (!current_skip_reaction && !dependency?.reactions?.includes(reaction)) { - // If we are working with an unowned signal as part of an effect (due to !current_skip_reaction) - // and the version hasn't changed, we still need to check that this reaction - // if linked to the dependency source – otherwise future updates will not be caught. - (dependency.reactions ??= []).push(reaction); - } + if (dependency.version > reaction.version) { + return true; } } } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-9/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-9/_config.js new file mode 100644 index 000000000000..ce3cfa122f44 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-9/_config.js @@ -0,0 +1,71 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + async test({ assert, target, logs }) { + let [btn1, btn2] = target.querySelectorAll('button'); + + btn1.click(); + btn2.click(); + + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `