Skip to content

Commit 9a4cd7e

Browse files
authored
chore: refactor effects a bit (#10948)
* WIP * formalise branch effects * WIP * rename MANAGED to BRANCH_EFFECT * remove ondestroy functions * tidy up * simplify * lint * tidy up * tidy up * tidy up * tidy up * remove ondestroy * tidy up * tidy up * remove TODO comment * update comment
1 parent b6598a3 commit 9a4cd7e

File tree

21 files changed

+316
-442
lines changed

21 files changed

+316
-442
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ export const javascript_visitors_runes = {
389389
const args = /** @type {import('estree').Expression[]} */ (
390390
node.arguments.map((arg) => context.visit(arg))
391391
);
392-
return b.call('$.user_root_effect', ...args);
392+
return b.call('$.effect_root', ...args);
393393
}
394394

395395
if (rune === '$inspect' || rune === '$inspect().with') {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ export const DERIVED = 1 << 1;
22
export const EFFECT = 1 << 2;
33
export const PRE_EFFECT = 1 << 3;
44
export const RENDER_EFFECT = 1 << 4;
5-
export const MANAGED = 1 << 6;
5+
export const BLOCK_EFFECT = 1 << 5;
6+
export const BRANCH_EFFECT = 1 << 6;
67
export const UNOWNED = 1 << 7;
78
export const CLEAN = 1 << 8;
89
export const DIRTY = 1 << 9;
@@ -11,6 +12,7 @@ export const INERT = 1 << 11;
1112
export const DESTROYED = 1 << 12;
1213
export const IS_ELSEIF = 1 << 13;
1314
export const EFFECT_RAN = 1 << 14;
15+
export const ROOT_EFFECT = 1 << 15;
1416

1517
export const UNINITIALIZED = Symbol();
1618
export const STATE_SYMBOL = Symbol('$state');

packages/svelte/src/internal/client/dom/blocks/await.js

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { is_promise } from '../../../common.js';
2-
import { remove } from '../reconciler.js';
32
import {
43
current_component_context,
54
flushSync,
65
set_current_component_context,
76
set_current_effect,
87
set_current_reaction
98
} from '../../runtime.js';
10-
import { destroy_effect, pause_effect, render_effect } from '../../reactivity/effects.js';
9+
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
1110
import { INERT } from '../../constants.js';
1211

1312
/**
@@ -39,10 +38,10 @@ export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
3938
* @param {any} value
4039
*/
4140
function create_effect(fn, value) {
42-
set_current_effect(branch);
43-
set_current_reaction(branch); // TODO do we need both?
41+
set_current_effect(effect);
42+
set_current_reaction(effect); // TODO do we need both?
4443
set_current_component_context(component_context);
45-
var effect = render_effect(() => fn(anchor, value), true);
44+
var e = branch(() => fn(anchor, value));
4645
set_current_component_context(null);
4746
set_current_reaction(null);
4847
set_current_effect(null);
@@ -51,22 +50,21 @@ export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
5150
// resolves which is unexpected behaviour (and somewhat irksome to test)
5251
flushSync();
5352

54-
return effect;
53+
return e;
5554
}
5655

57-
const branch = render_effect(() => {
56+
const effect = block(() => {
5857
if (input === (input = get_input())) return;
5958

6059
if (is_promise(input)) {
6160
const promise = /** @type {Promise<any>} */ (input);
6261

6362
if (pending_fn) {
6463
if (pending_effect && (pending_effect.f & INERT) === 0) {
65-
if (pending_effect.dom) remove(pending_effect.dom);
6664
destroy_effect(pending_effect);
6765
}
6866

69-
pending_effect = render_effect(() => pending_fn(anchor), true);
67+
pending_effect = branch(() => pending_fn(anchor));
7068
}
7169

7270
if (then_effect) pause_effect(then_effect);
@@ -96,19 +94,11 @@ export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
9694

9795
if (then_fn) {
9896
if (then_effect) {
99-
if (then_effect.dom) remove(then_effect.dom);
10097
destroy_effect(then_effect);
10198
}
10299

103-
then_effect = render_effect(() => then_fn(anchor, input), true);
100+
then_effect = branch(() => then_fn(anchor, input));
104101
}
105102
}
106103
});
107-
108-
branch.ondestroy = () => {
109-
// TODO this sucks, tidy it up
110-
if (pending_effect?.dom) remove(pending_effect.dom);
111-
if (then_effect?.dom) remove(then_effect.dom);
112-
if (catch_effect?.dom) remove(catch_effect.dom);
113-
};
114104
}

packages/svelte/src/internal/client/dom/blocks/css-props.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,28 @@ export function css_props(anchor, is_html, props, component) {
3939

4040
component(component_anchor);
4141

42-
/** @type {Record<string, string>} */
43-
let current_props = {};
42+
render_effect(() => {
43+
/** @type {Record<string, string>} */
44+
let current_props = {};
4445

45-
const effect = render_effect(() => {
46-
const next_props = props();
46+
render_effect(() => {
47+
const next_props = props();
4748

48-
for (const key in current_props) {
49-
if (!(key in next_props)) {
50-
element.style.removeProperty(key);
49+
for (const key in current_props) {
50+
if (!(key in next_props)) {
51+
element.style.removeProperty(key);
52+
}
5153
}
52-
}
5354

54-
for (const key in next_props) {
55-
element.style.setProperty(key, next_props[key]);
56-
}
55+
for (const key in next_props) {
56+
element.style.setProperty(key, next_props[key]);
57+
}
5758

58-
current_props = next_props;
59-
});
59+
current_props = next_props;
60+
});
6061

61-
effect.ondestroy = () => {
62-
remove(element);
63-
};
62+
return () => {
63+
remove(element);
64+
};
65+
});
6466
}

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 27 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import { empty } from '../operations.js';
1111
import { insert, remove } from '../reconciler.js';
1212
import { untrack } from '../../runtime.js';
1313
import {
14-
destroy_effect,
14+
block,
15+
branch,
1516
effect,
1617
pause_effect,
1718
pause_effects,
18-
render_effect,
1919
resume_effect
2020
} from '../../reactivity/effects.js';
2121
import { source, mutable_source, set } from '../../reactivity/sources.js';
@@ -43,7 +43,7 @@ export function set_current_each_item(item) {
4343
* @param {number} flags
4444
* @param {() => V[]} get_collection
4545
* @param {null | ((item: V) => string)} get_key
46-
* @param {(anchor: Node, item: V, index: import('#client').MaybeSource<number>) => void} render_fn
46+
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: import('#client').MaybeSource<number>) => void} render_fn
4747
* @param {null | ((anchor: Node) => void)} fallback_fn
4848
* @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn
4949
* @returns {void}
@@ -67,7 +67,7 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
6767
/** @type {import('#client').Effect | null} */
6868
var fallback = null;
6969

70-
var effect = render_effect(() => {
70+
block(() => {
7171
var collection = get_collection();
7272

7373
var array = is_array(collection)
@@ -152,15 +152,7 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
152152
if (fallback) {
153153
resume_effect(fallback);
154154
} else {
155-
fallback = render_effect(() => {
156-
var dom = fallback_fn(anchor);
157-
158-
return () => {
159-
if (dom !== undefined) {
160-
remove(dom);
161-
}
162-
};
163-
}, true);
155+
fallback = branch(() => fallback_fn(anchor));
164156
}
165157
} else if (fallback !== null) {
166158
pause_effect(fallback, () => {
@@ -174,17 +166,6 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
174166
set_hydrating(true);
175167
}
176168
});
177-
178-
effect.ondestroy = () => {
179-
for (var item of state.items) {
180-
if (item.e.dom !== null) {
181-
remove(item.e.dom);
182-
destroy_effect(item.e);
183-
}
184-
}
185-
186-
if (fallback) destroy_effect(fallback);
187-
};
188169
}
189170

190171
/**
@@ -193,7 +174,7 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
193174
* @param {number} flags
194175
* @param {() => V[]} get_collection
195176
* @param {null | ((item: V) => string)} get_key
196-
* @param {(anchor: Node, item: V, index: import('#client').MaybeSource<number>) => void} render_fn
177+
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: import('#client').MaybeSource<number>) => void} render_fn
197178
* @param {null | ((anchor: Node) => void)} [fallback_fn]
198179
* @returns {void}
199180
*/
@@ -206,7 +187,7 @@ export function each_keyed(anchor, flags, get_collection, get_key, render_fn, fa
206187
* @param {Element | Comment} anchor
207188
* @param {number} flags
208189
* @param {() => V[]} get_collection
209-
* @param {(anchor: Node, item: V, index: import('#client').MaybeSource<number>) => void} render_fn
190+
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: import('#client').MaybeSource<number>) => void} render_fn
210191
* @param {null | ((anchor: Node) => void)} [fallback_fn]
211192
* @returns {void}
212193
*/
@@ -219,7 +200,7 @@ export function each_indexed(anchor, flags, get_collection, render_fn, fallback_
219200
* @param {Array<V>} array
220201
* @param {import('#client').EachState} state
221202
* @param {Element | Comment | Text} anchor
222-
* @param {(anchor: Node, item: V, index: number | import('#client').Source<number>) => void} render_fn
203+
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: number | import('#client').Source<number>) => void} render_fn
223204
* @param {number} flags
224205
* @returns {void}
225206
*/
@@ -275,7 +256,7 @@ function reconcile_indexed_array(array, state, anchor, render_fn, flags) {
275256
* @param {Array<V>} array
276257
* @param {import('#client').EachState} state
277258
* @param {Element | Comment | Text} anchor
278-
* @param {(anchor: Node, item: V, index: number | import('#client').Source<number>) => void} render_fn
259+
* @param {(anchor: Node, item: import('#client').MaybeSource<V>, index: number | import('#client').Source<number>) => void} render_fn
279260
* @param {number} flags
280261
* @param {any[]} keys
281262
* @returns {void}
@@ -342,7 +323,6 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
342323
var i;
343324
var index;
344325
var last_item;
345-
var last_sibling;
346326

347327
// store the indexes of each item in the new world
348328
for (i = start; i < b; i += 1) {
@@ -548,45 +528,32 @@ function update_item(item, value, index, type) {
548528
* @param {V} value
549529
* @param {unknown} key
550530
* @param {number} index
551-
* @param {(anchor: Node, item: V, index: number | import('#client').Value<number>) => void} render_fn
531+
* @param {(anchor: Node, item: V | import('#client').Source<V>, index: number | import('#client').Value<number>) => void} render_fn
552532
* @param {number} flags
553533
* @returns {import('#client').EachItem}
554534
*/
555535
function create_item(anchor, value, key, index, render_fn, flags) {
556-
var each_item_not_reactive = (flags & EACH_ITEM_REACTIVE) === 0;
557-
558-
/** @type {import('#client').EachItem} */
559-
var item = {
560-
a: null,
561-
// dom
562-
// @ts-expect-error
563-
e: null,
564-
// index
565-
i: (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index),
566-
// key
567-
k: key,
568-
// item
569-
v: each_item_not_reactive
570-
? value
571-
: (flags & EACH_IS_STRICT_EQUALS) !== 0
572-
? source(value)
573-
: mutable_source(value)
574-
};
575-
576536
var previous_each_item = current_each_item;
577537

578538
try {
579-
current_each_item = item;
580-
581-
item.e = render_effect(() => {
582-
var dom = render_fn(anchor, item.v, item.i);
539+
var reactive = (flags & EACH_ITEM_REACTIVE) !== 0;
540+
var mutable = (flags & EACH_IS_STRICT_EQUALS) === 0;
541+
542+
var v = reactive ? (mutable ? mutable_source(value) : source(value)) : value;
543+
var i = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
544+
545+
/** @type {import('#client').EachItem} */
546+
var item = {
547+
i,
548+
v,
549+
k: key,
550+
a: null,
551+
// @ts-expect-error
552+
e: null
553+
};
583554

584-
return () => {
585-
if (dom !== undefined) {
586-
remove(dom);
587-
}
588-
};
589-
}, true);
555+
current_each_item = item;
556+
item.e = branch(() => render_fn(anchor, v, i));
590557

591558
return item;
592559
} finally {
Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,22 @@
1+
import { derived } from '../../reactivity/deriveds.js';
12
import { render_effect } from '../../reactivity/effects.js';
3+
import { get } from '../../runtime.js';
24
import { reconcile_html, remove } from '../reconciler.js';
35

46
/**
5-
* @param {Element | Text | Comment} dom
7+
* @param {Element | Text | Comment} anchor
68
* @param {() => string} get_value
79
* @param {boolean} svg
810
* @returns {void}
911
*/
10-
export function html(dom, get_value, svg) {
11-
/** @type {import('#client').Dom} */
12-
let html_dom;
12+
export function html(anchor, get_value, svg) {
13+
let value = derived(get_value);
1314

14-
/** @type {string} */
15-
let value;
15+
render_effect(() => {
16+
var dom = reconcile_html(anchor, get(value), svg);
1617

17-
const effect = render_effect(() => {
18-
if (value !== (value = get_value())) {
19-
if (html_dom) remove(html_dom);
20-
html_dom = reconcile_html(dom, value, svg);
18+
if (dom) {
19+
return () => remove(dom);
2120
}
2221
});
23-
24-
effect.ondestroy = () => {
25-
if (html_dom) {
26-
remove(html_dom);
27-
}
28-
};
2922
}

0 commit comments

Comments
 (0)