diff --git a/.changeset/clean-badgers-battle.md b/.changeset/clean-badgers-battle.md new file mode 100644 index 000000000000..be9b7de2cbed --- /dev/null +++ b/.changeset/clean-badgers-battle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure effect_tracking correctly handles tracking reactions diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 2050c142c586..82baeae28805 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -15,7 +15,8 @@ import { set_is_destroying_effect, set_is_flushing_effect, set_signal_status, - untrack + untrack, + skip_reaction } from '../runtime.js'; import { DIRTY, @@ -167,7 +168,9 @@ export function effect_tracking() { return false; } - return (active_reaction.f & UNOWNED) === 0; + // If it's skipped, that's because we're inside an unowned + // that is not being tracked by another reaction + return !skip_reaction; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js new file mode 100644 index 000000000000..7e057e94b2c6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/_config.js @@ -0,0 +1,19 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + let btn1 = target.querySelector('button'); + + btn1?.click(); + flushSync(); + + btn1?.click(); + flushSync(); + + btn1?.click(); + flushSync(); + + assert.deepEqual(logs, ['light', 'dark', 'light']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte new file mode 100644 index 000000000000..d35c706aecfb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/main.svelte @@ -0,0 +1,13 @@ + + + + +{themeState.value.theme} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js new file mode 100644 index 000000000000..30c187cbe913 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-10/theme.svelte.js @@ -0,0 +1,18 @@ +import { fromStore, writable } from 'svelte/store'; + +export const store = writable({ theme: 'dark' }); + +class ThemeState { + #storeState = fromStore(store); + value = $derived(this.#storeState.current); + + constructor() { + $effect.root(() => { + $effect(() => { + console.log(this.value.theme); + }); + }); + } +} + +export const themeState = new ThemeState();