From 18fae091f60db6fca02f09f7e2e03bf37157a5fb Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 23 Sep 2025 20:10:01 +0200 Subject: [PATCH 1/2] fix: depend on reads of deriveds created within reaction (async mode) As part of https://github.com/sveltejs/kit/pull/14481 we discovered that deriveds created within reactions and reading from them in that same reaction is actually useful in some cases, as such a use case we couldn't imagine yet in #15564 has appeared. We think it's ultimately better to rerun on those cases, so we're going to make this change in async mode (that way the behavior doesn't change unless you have enabled the experimental flag) --- .changeset/gold-eels-lay.md | 5 ++ .../internal/client/reactivity/deriveds.js | 4 +- .../samples/derived-in-expression/_config.js | 75 +++++++++++++++++++ .../samples/derived-in-expression/main.svelte | 30 ++++++++ .../samples/untrack-own-deriveds/_config.js | 3 +- .../samples/untrack-own-deriveds/main.svelte | 6 +- 6 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 .changeset/gold-eels-lay.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-in-expression/main.svelte diff --git a/.changeset/gold-eels-lay.md b/.changeset/gold-eels-lay.md new file mode 100644 index 000000000000..ef6ceb4dab2d --- /dev/null +++ b/.changeset/gold-eels-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: depend on reads of deriveds created within reaction (async mode) diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 299251a2dcdd..11405a8e664f 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -29,7 +29,7 @@ import * as w from '../warnings.js'; import { async_effect, destroy_effect } from './effects.js'; import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js'; import { get_stack } from '../dev/tracing.js'; -import { tracing_mode_flag } from '../../flags/index.js'; +import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { Boundary } from '../dom/blocks/boundary.js'; import { component_context } from '../context.js'; import { UNINITIALIZED } from '../../../constants.js'; @@ -231,7 +231,7 @@ export function async_derived(fn, location) { export function user_derived(fn) { const d = derived(fn); - push_reaction_value(d); + if (!async_mode_flag) push_reaction_value(d); return d; } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js new file mode 100644 index 000000000000..c4a821349cd8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js @@ -0,0 +1,75 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + // In non-async mode we're not reacting to deriveds read in the same context they're defined in + skip_no_async: true, + solo: true, + test({ assert, target, logs }) { + const [a, b] = target.querySelectorAll('button'); + + flushSync(() => a?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

1/0

a?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

2/0

b?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

2/1

b?.click()); + assert.htmlEqual( + target.innerHTML, + ` + + +

2/2

+ let object = $state.raw({ a: 0, b: 0 }); + + function a() { + console.log('a'); + return object.a; + } + + function b() { + console.log('b'); + let double = $derived(object.b) + return double; + } + + $effect(() => { + object.a; + console.log('effect a'); + }) + + $effect(() => { + const b = $derived(object.b); + b; + console.log('effect b'); + }) + + + + + +

{a()}/{b()}

diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js index b728c3c0bead..a6bfda138173 100644 --- a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js @@ -14,9 +14,10 @@ export default test({ `

1/2

1/2

{ foo = new Foo(); }); + + let bar = $derived(new Foo()); - + {#if foo}

{foo.value}/{foo.double}

{/if} + +

{bar.value}/{bar.double}

From eac39305ecb56f51d2838c9bc4d70715fb3b8cba Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 23 Sep 2025 20:18:39 +0200 Subject: [PATCH 2/2] fix tests --- .../runtime-runes/samples/derived-in-expression/_config.js | 1 - .../runtime-runes/samples/untrack-own-deriveds/_config.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js index c4a821349cd8..73428e0ff214 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/derived-in-expression/_config.js @@ -4,7 +4,6 @@ import { test } from '../../test'; export default test({ // In non-async mode we're not reacting to deriveds read in the same context they're defined in skip_no_async: true, - solo: true, test({ assert, target, logs }) { const [a, b] = target.querySelectorAll('button'); diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js index a6bfda138173..b5233b01e8b6 100644 --- a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js @@ -13,8 +13,8 @@ export default test({ target.innerHTML, ` -

1/2

1/2

1/2

+

1/2

` );