diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index fafd3d1442..fd478fc3dc 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -53,6 +53,7 @@ export function push_effect(effect, parent_effect) { */ function create_effect(type, fn, sync) { var is_root = (type & ROOT_EFFECT) !== 0; + /** @type {import('#client').Effect} */ var effect = { ctx: current_component_context, @@ -150,9 +151,7 @@ export function user_pre_effect(fn) { * @returns {() => void} */ export function effect_root(fn) { - // TODO is `untrack` correct here? Should `fn` re-run if its dependencies change? - // Should it even be modelled as an effect? - const effect = create_effect(ROOT_EFFECT, () => untrack(fn), true); + const effect = create_effect(ROOT_EFFECT, fn, true); return () => { destroy_effect(effect); }; diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 2f28137163..6465078465 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -245,27 +245,25 @@ function _mount( const unmount = effect_root(() => { branch(() => { - untrack(() => { - if (context) { - push({}); - var ctx = /** @type {import('#client').ComponentContext} */ (current_component_context); - ctx.c = context; - } - - if (events) { - // We can't spread the object or else we'd lose the state proxy stuff, if it is one - /** @type {any} */ (props).$$events = events; - } - - should_intro = intro; - // @ts-expect-error the public typings are not what the actual function looks like - component = Component(anchor, props) || {}; - should_intro = true; - - if (context) { - pop(); - } - }); + if (context) { + push({}); + var ctx = /** @type {import('#client').ComponentContext} */ (current_component_context); + ctx.c = context; + } + + if (events) { + // We can't spread the object or else we'd lose the state proxy stuff, if it is one + /** @type {any} */ (props).$$events = events; + } + + should_intro = intro; + // @ts-expect-error the public typings are not what the actual function looks like + component = Component(anchor, props) || {}; + should_intro = true; + + if (context) { + pop(); + } }); return () => { diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a41f8a6f7f..8f4c275810 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -21,7 +21,8 @@ import { INERT, BRANCH_EFFECT, STATE_SYMBOL, - BLOCK_EFFECT + BLOCK_EFFECT, + ROOT_EFFECT } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { add_owner } from './dev/ownership.js'; @@ -692,7 +693,7 @@ export function get(signal) { // Register the dependency on the current reaction signal. if ( current_reaction !== null && - (current_reaction.f & BRANCH_EFFECT) === 0 && + (current_reaction.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 && !current_untracking ) { const unowned = (current_reaction.f & UNOWNED) !== 0; @@ -741,6 +742,7 @@ export function get(signal) { update_derived(/** @type {import('./types.js').Derived} **/ (signal), false); } } + return signal.v; } diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-2/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root-2/_config.js new file mode 100644 index 0000000000..f6d99a0f5d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-2/_config.js @@ -0,0 +1,31 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + before_test() { + log.length = 0; + }, + + async test({ assert, target }) { + const [b1, b2] = target.querySelectorAll('button'); + + flushSync(() => { + b1.click(); + }); + + assert.deepEqual(log, [0]); + + flushSync(() => { + b2.click(); + }); + + assert.deepEqual(log, [0, 'cleanup']); + + flushSync(() => { + b1.click(); + }); + + assert.deepEqual(log, [0, 'cleanup']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-2/log.js b/packages/svelte/tests/runtime-runes/samples/effect-root-2/log.js new file mode 100644 index 0000000000..d3df521f4d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-2/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-2/main.svelte new file mode 100644 index 0000000000..591975ce54 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-2/main.svelte @@ -0,0 +1,13 @@ + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte index 7a93718659..f7e8275aae 100644 --- a/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/effect-root/main.svelte @@ -11,7 +11,7 @@ const nested_cleanup = $effect.root(() => { return () => { - log.push('cleanup 2') ; + log.push('cleanup 2'); } }); @@ -22,6 +22,6 @@ }); - - - + + +