diff --git a/.changeset/nasty-clocks-exercise.md b/.changeset/nasty-clocks-exercise.md new file mode 100644 index 0000000000..7362f8c0b2 --- /dev/null +++ b/.changeset/nasty-clocks-exercise.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: corrects a beforeUpdate/afterUpdate bug diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a67f300b21..30807a317a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -960,10 +960,18 @@ export function set_signal_value(signal, value) { schedule_effect(current_effect, false); } mark_signal_consumers(signal, DIRTY, true); - // If we have afterUpdates locally on the component, but we're within a render effect - // then we will need to manually invoke the beforeUpdate/afterUpdate logic. + // This logic checks if there are any render effects queued after the above marking + // of consumers. If there are render effects that have the same component context as + // the source signal we're writing to, then we can bail-out of this logic as there + // will be a render effect in the queue that hopefully takes case of triggering the + // beforeUpdate/afterUpdate logic (doing it again here would duplicate them). However, + // if the render effects scheduled in the queue are unrelated to the component context, + // then we need to trigger the beforeUpdate/afterUpdate logic here instead. // TODO: should we put this being a is_runes check and only run it in non-runes mode? - if (current_effect === null && current_queued_pre_and_render_effects.length === 0) { + if ( + current_effect === null && + current_queued_pre_and_render_effects.every((e) => e.context !== component_context) + ) { const update_callbacks = component_context?.update_callbacks; if (update_callbacks != null) { update_callbacks.before.forEach(/** @param {any} c */ (c) => c()); diff --git a/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/Child.svelte b/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/Child.svelte new file mode 100644 index 0000000000..5f27f07c9d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/Child.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/_config.js b/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/_config.js new file mode 100644 index 0000000000..5a357f88c4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/_config.js @@ -0,0 +1,15 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: '', + + async test({ assert, target, component }) { + const [btn] = target.querySelectorAll('button'); + flushSync(() => { + btn.click(); + }); + assert.deepEqual(component.log, ['beforeUpdate', 'afterUpdate']); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/main.svelte new file mode 100644 index 0000000000..b3e6f583fd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/runes-before-after-update/main.svelte @@ -0,0 +1,22 @@ + + +