diff --git a/.changeset/khaki-camels-punch.md b/.changeset/khaki-camels-punch.md new file mode 100644 index 0000000000..ee761ccc7e --- /dev/null +++ b/.changeset/khaki-camels-punch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure correct parent effect is associated with render effects diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 225137ec1e..fa43f0afb2 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -201,7 +201,10 @@ export function user_effect(fn) { if (defer) { var context = /** @type {ComponentContext} */ (component_context); - (context.e ??= []).push(fn); + (context.e ??= []).push({ + fn, + parent: active_effect + }); } else { var signal = effect(fn); return signal; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index ded38a6764..bfea047b24 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1050,11 +1050,18 @@ export function pop(component) { if (component !== undefined) { context_stack_item.x = component; } - const effects = context_stack_item.e; - if (effects !== null) { + const component_effects = context_stack_item.e; + if (component_effects !== null) { + var previous_effect = active_effect; context_stack_item.e = null; - for (var i = 0; i < effects.length; i++) { - effect(effects[i]); + try { + for (var i = 0; i < component_effects.length; i++) { + var component_effect = component_effects[i]; + set_active_effect(component_effect.parent); + effect(component_effect.fn); + } + } finally { + set_active_effect(previous_effect); } } component_context = context_stack_item.p; diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 9a3955d9ca..6d7065f1c8 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -15,7 +15,7 @@ export type ComponentContext = { /** context */ c: null | Map; /** deferred effects */ - e: null | Array<() => void | (() => void)>; + e: null | Array<{ fn: () => void | (() => void); parent: null | Effect }>; /** mounted */ m: boolean; /** diff --git a/packages/svelte/tests/runtime-runes/samples/effect-order-4/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-order-4/_config.js new file mode 100644 index 0000000000..c1480c19a7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-order-4/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, logs }) { + const [b1] = target.querySelectorAll('button'); + flushSync(() => { + b1.click(); + }); + flushSync(() => { + b1.click(); + }); + assert.deepEqual(logs, [ + { count: 0, doubled: 0 }, + { count: 1, doubled: 2 }, + { count: 2, doubled: 4 } + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-order-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-order-4/main.svelte new file mode 100644 index 0000000000..31bd1f6668 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-order-4/main.svelte @@ -0,0 +1,18 @@ + + +