Skip to content

Commit 1579278

Browse files
authored
more effect ordering stuff (#10958)
* WIP * i guess this change makes sense? * simplify
1 parent 6152e7d commit 1579278

File tree

6 files changed

+49
-28
lines changed

6 files changed

+49
-28
lines changed

packages/svelte/src/internal/client/dom/elements/bindings/this.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function is_bound_this(bound_value, element_or_component) {
2222
* @returns {void}
2323
*/
2424
export function bind_this(element_or_component, update, get_value, get_parts) {
25-
render_effect(() => {
25+
effect(() => {
2626
/** @type {unknown[]} */
2727
var old_parts;
2828

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

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,9 @@ import { remove } from '../dom/reconciler.js';
3434
* @param {import('./types.js').EffectType} type
3535
* @param {(() => void | (() => void))} fn
3636
* @param {boolean} sync
37-
* @param {boolean} init
3837
* @returns {import('#client').Effect}
3938
*/
40-
function create_effect(type, fn, sync, init = true) {
39+
function create_effect(type, fn, sync) {
4140
var is_root = (type & ROOT_EFFECT) !== 0;
4241
/** @type {import('#client').Effect} */
4342
var effect = {
@@ -61,20 +60,18 @@ function create_effect(type, fn, sync, init = true) {
6160
}
6261
}
6362

64-
if (init) {
65-
if (sync) {
66-
var previously_flushing_effect = is_flushing_effect;
63+
if (sync) {
64+
var previously_flushing_effect = is_flushing_effect;
6765

68-
try {
69-
set_is_flushing_effect(true);
70-
execute_effect(effect);
71-
effect.f |= EFFECT_RAN;
72-
} finally {
73-
set_is_flushing_effect(previously_flushing_effect);
74-
}
75-
} else {
76-
schedule_effect(effect);
66+
try {
67+
set_is_flushing_effect(true);
68+
execute_effect(effect);
69+
effect.f |= EFFECT_RAN;
70+
} finally {
71+
set_is_flushing_effect(previously_flushing_effect);
7772
}
73+
} else {
74+
schedule_effect(effect);
7875
}
7976

8077
return effect;
@@ -91,7 +88,6 @@ export function effect_active() {
9188
/**
9289
* Internal representation of `$effect(...)`
9390
* @param {() => void | (() => void)} fn
94-
* @returns {import('#client').Effect}
9591
*/
9692
export function user_effect(fn) {
9793
if (current_effect === null) {
@@ -101,7 +97,20 @@ export function user_effect(fn) {
10197
);
10298
}
10399

104-
return create_effect(EFFECT, fn, false, true);
100+
// Non-nested `$effect(...)` in a component should be deferred
101+
// until the component is mounted
102+
const defer =
103+
current_effect.f & RENDER_EFFECT &&
104+
// TODO do we actually need this? removing them changes nothing
105+
current_component_context !== null &&
106+
!current_component_context.m;
107+
108+
if (defer) {
109+
const context = /** @type {import('#client').ComponentContext} */ (current_component_context);
110+
(context.e ??= []).push(fn);
111+
} else {
112+
effect(fn);
113+
}
105114
}
106115

107116
/**

packages/svelte/src/internal/client/runtime.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
object_prototype
99
} from './utils.js';
1010
import { unstate } from './proxy.js';
11-
import { destroy_effect, user_pre_effect } from './reactivity/effects.js';
11+
import { destroy_effect, effect, user_pre_effect } from './reactivity/effects.js';
1212
import {
1313
EFFECT,
1414
RENDER_EFFECT,
@@ -1094,6 +1094,8 @@ export function push(props, runes = false, fn) {
10941094
x: null,
10951095
// context
10961096
c: null,
1097+
// effects
1098+
e: null,
10971099
// mounted
10981100
m: false,
10991101
// parent
@@ -1129,6 +1131,13 @@ export function pop(component) {
11291131
if (component !== undefined) {
11301132
context_stack_item.x = component;
11311133
}
1134+
const effects = context_stack_item.e;
1135+
if (effects !== null) {
1136+
context_stack_item.e = null;
1137+
for (let i = 0; i < effects.length; i++) {
1138+
effect(effects[i]);
1139+
}
1140+
}
11321141
current_component_context = context_stack_item.p;
11331142
context_stack_item.m = true;
11341143
}

packages/svelte/src/internal/client/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export type ComponentContext = {
2020
s: Record<string, unknown>;
2121
/** exports (and props, if `accessors: true`) */
2222
x: Record<string, any> | null;
23+
/** deferred effects */
24+
e: null | Array<() => void | (() => void)>;
2325
/** mounted */
2426
m: boolean;
2527
/** parent */

packages/svelte/tests/runtime-legacy/samples/ondestroy-deep/_config.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ import { test } from '../../test';
22
import { destroyed, reset } from './destroyed.js';
33

44
export default test({
5-
test({ assert, component }) {
6-
// for hydration, ssr may have pushed to `destroyed`
5+
before_test() {
76
reset();
7+
},
88

9+
test({ assert, component }) {
910
component.visible = false;
1011
assert.deepEqual(destroyed, ['A', 'B', 'C']);
11-
12-
reset();
1312
},
1413

1514
test_ssr({ assert }) {

packages/svelte/tests/signals/test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { describe, assert, it } from 'vitest';
22
import * as $ from '../../src/internal/client/runtime';
33
import {
4-
destroy_effect,
54
effect,
5+
effect_root,
66
render_effect,
77
user_effect
88
} from '../../src/internal/client/reactivity/effects';
@@ -22,12 +22,12 @@ function run_test(runes: boolean, fn: (runes: boolean) => () => void) {
2222
$.push({}, runes);
2323
// Create a render context so that effect validations etc don't fail
2424
let execute: any;
25-
const signal = render_effect(() => {
25+
const destroy = effect_root(() => {
2626
execute = fn(runes);
2727
});
2828
$.pop();
2929
execute();
30-
destroy_effect(signal);
30+
destroy();
3131
};
3232
}
3333

@@ -248,8 +248,10 @@ describe('signals', () => {
248248
test('effect with derived using unowned derived every time', () => {
249249
const log: Array<number | string> = [];
250250

251-
const effect = user_effect(() => {
252-
log.push($.get(calc));
251+
const destroy = effect_root(() => {
252+
user_effect(() => {
253+
log.push($.get(calc));
254+
});
253255
});
254256

255257
return () => {
@@ -261,7 +263,7 @@ describe('signals', () => {
261263
// Ensure we're not leaking consumers
262264
assert.deepEqual(count.reactions?.length, 1);
263265
assert.deepEqual(log, [0, 2, 'limit', 0]);
264-
destroy_effect(effect);
266+
destroy();
265267
// Ensure we're not leaking consumers
266268
assert.deepEqual(count.reactions, null);
267269
};

0 commit comments

Comments
 (0)