diff --git a/.changeset/tasty-steaks-smile.md b/.changeset/tasty-steaks-smile.md new file mode 100644 index 0000000000..38ece02f26 --- /dev/null +++ b/.changeset/tasty-steaks-smile.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: adjust render effect ordering diff --git a/packages/svelte/src/internal/client/dom/legacy/lifecycle.js b/packages/svelte/src/internal/client/dom/legacy/lifecycle.js index d7ec2efa08..425f65398a 100644 --- a/packages/svelte/src/internal/client/dom/legacy/lifecycle.js +++ b/packages/svelte/src/internal/client/dom/legacy/lifecycle.js @@ -1,6 +1,12 @@ import { run } from '../../../common.js'; import { pre_effect, user_effect } from '../../reactivity/effects.js'; -import { current_component_context, deep_read_state, get, untrack } from '../../runtime.js'; +import { + current_component_context, + deep_read_state, + flush_local_render_effects, + get, + untrack +} from '../../runtime.js'; /** * Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects @@ -16,6 +22,10 @@ export function init() { pre_effect(() => { observe_all(context); callbacks.b.forEach(run); + // beforeUpdate might change state that affects rendering, ensure the render effects following from it + // are batched up with the current run. Avoids for example child components rerunning when they're + // now hidden because beforeUpdate did set an if block to false. + flush_local_render_effects(); }); } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 8875e03ded..99c98013d4 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -148,18 +148,9 @@ export function pre_effect(fn) { : '') ); } - const sync = current_effect !== null && (current_effect.f & RENDER_EFFECT) !== 0; - return create_effect( - PRE_EFFECT, - () => { - const val = fn(); - flush_local_render_effects(); - return val; - }, - sync - ); + return create_effect(PRE_EFFECT, fn, sync); } /** diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 5ec8193f0e..dcd544671d 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -82,9 +82,11 @@ export class ReactiveSet extends Set { /** @param {T} value */ has(value) { var source = this.#sources.get(value); + // We should always track the version in case + // the Set ever gets this value in the future. + get(this.#version); if (source === undefined) { - get(this.#version); return false; } diff --git a/packages/svelte/src/reactivity/set.test.ts b/packages/svelte/src/reactivity/set.test.ts index 625a90db7d..cea7221060 100644 --- a/packages/svelte/src/reactivity/set.test.ts +++ b/packages/svelte/src/reactivity/set.test.ts @@ -30,9 +30,7 @@ test('set.values()', () => { set.clear(); }); - // TODO looks like another effect ordering bug — sequence should be , - // but values is reversed at end - assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, [], false]); + assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, false, []]); cleanup(); }); diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/_config.js new file mode 100644 index 0000000000..bc62b4f2c9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + get props() { + return { n: 0 }; + }, + + before_test() { + log.length = 0; + }, + + async test({ assert, component }) { + assert.deepEqual(log, ['$effect.pre 0', 'another $effect.pre 1', 'render n0', 'render i1']); + + log.length = 0; + component.n += 1; + + assert.deepEqual(log, ['$effect.pre 1', 'another $effect.pre 2', 'render n1', 'render i2']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/log.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/log.js new file mode 100644 index 0000000000..d3df521f4d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/main.svelte new file mode 100644 index 0000000000..ecbd9321bf --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-5/main.svelte @@ -0,0 +1,19 @@ + + +

{logRender(`n${n}`)}

+

{logRender(`i${i}`)}