Skip to content

Commit 2cb78ac

Browse files
authored
fix: adjust render effect ordering (#10783)
We can simplify pre effects by not doing the flush logic at all now. Instead we can move the flushing logic to the only place its needed – for beforeUpdate
1 parent 0c1026f commit 2cb78ac

File tree

8 files changed

+63
-15
lines changed

8 files changed

+63
-15
lines changed

.changeset/tasty-steaks-smile.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: adjust render effect ordering

packages/svelte/src/internal/client/dom/legacy/lifecycle.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { run } from '../../../common.js';
22
import { pre_effect, user_effect } from '../../reactivity/effects.js';
3-
import { current_component_context, deep_read_state, get, untrack } from '../../runtime.js';
3+
import {
4+
current_component_context,
5+
deep_read_state,
6+
flush_local_render_effects,
7+
get,
8+
untrack
9+
} from '../../runtime.js';
410

511
/**
612
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
@@ -16,6 +22,10 @@ export function init() {
1622
pre_effect(() => {
1723
observe_all(context);
1824
callbacks.b.forEach(run);
25+
// beforeUpdate might change state that affects rendering, ensure the render effects following from it
26+
// are batched up with the current run. Avoids for example child components rerunning when they're
27+
// now hidden because beforeUpdate did set an if block to false.
28+
flush_local_render_effects();
1929
});
2030
}
2131

packages/svelte/src/internal/client/reactivity/effects.js

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,9 @@ export function pre_effect(fn) {
148148
: '')
149149
);
150150
}
151-
152151
const sync = current_effect !== null && (current_effect.f & RENDER_EFFECT) !== 0;
153152

154-
return create_effect(
155-
PRE_EFFECT,
156-
() => {
157-
const val = fn();
158-
flush_local_render_effects();
159-
return val;
160-
},
161-
sync
162-
);
153+
return create_effect(PRE_EFFECT, fn, sync);
163154
}
164155

165156
/**

packages/svelte/src/reactivity/set.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,11 @@ export class ReactiveSet extends Set {
8282
/** @param {T} value */
8383
has(value) {
8484
var source = this.#sources.get(value);
85+
// We should always track the version in case
86+
// the Set ever gets this value in the future.
87+
get(this.#version);
8588

8689
if (source === undefined) {
87-
get(this.#version);
8890
return false;
8991
}
9092

packages/svelte/src/reactivity/set.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ test('set.values()', () => {
3030
set.clear();
3131
});
3232

33-
// TODO looks like another effect ordering bug — sequence should be <size, has, values>,
34-
// but values is reversed at end
35-
assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, [], false]);
33+
assert.deepEqual(log, [5, true, [1, 2, 3, 4, 5], 4, false, [1, 2, 4, 5], 0, false, []]);
3634

3735
cleanup();
3836
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { test } from '../../test';
2+
import { log } from './log.js';
3+
4+
export default test({
5+
get props() {
6+
return { n: 0 };
7+
},
8+
9+
before_test() {
10+
log.length = 0;
11+
},
12+
13+
async test({ assert, component }) {
14+
assert.deepEqual(log, ['$effect.pre 0', 'another $effect.pre 1', 'render n0', 'render i1']);
15+
16+
log.length = 0;
17+
component.n += 1;
18+
19+
assert.deepEqual(log, ['$effect.pre 1', 'another $effect.pre 2', 'render n1', 'render i2']);
20+
}
21+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/** @type {any[]} */
2+
export const log = [];
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script>
2+
import { untrack } from 'svelte';
3+
import { log } from './log.js';
4+
let { n = 0 } = $props();
5+
let i = $state(0);
6+
function logRender(i) {
7+
log.push(`render ${i}`);
8+
}
9+
$effect.pre(() => {
10+
log.push(`$effect.pre ${n}`);
11+
untrack(() => i++)
12+
});
13+
$effect.pre(() => {
14+
log.push('another $effect.pre '+ i);
15+
})
16+
</script>
17+
18+
<p>{logRender(`n${n}`)}</p>
19+
<p>{logRender(`i${i}`)}</p>

0 commit comments

Comments
 (0)