Skip to content

Commit 7019894

Browse files
authored
fix: ensure unowned deriveds can add themselves as reactions while connected (#16249)
an unowned derived should still add itself as a reaction while it is properly connected, which means it can be cleaned up correctly - and this connection is indicated by it having reactions. Fixes #15829 and potentially #15853
1 parent 32481be commit 7019894

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

.changeset/little-kings-smoke.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: ensure unowned deriveds can add themselves as reactions while connected

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,12 @@ export function update_reaction(reaction) {
294294
reaction.deps = deps = new_deps;
295295
}
296296

297-
if (!skip_reaction) {
297+
if (
298+
!skip_reaction ||
299+
// Deriveds that already have reactions can cleanup, so we still add them as reactions
300+
((flags & DERIVED) !== 0 &&
301+
/** @type {import('#client').Derived} */ (reaction).reactions !== null)
302+
) {
298303
for (i = skipped_deps; i < deps.length; i++) {
299304
(deps[i].reactions ??= []).push(reaction);
300305
}

packages/svelte/tests/signals/test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,45 @@ describe('signals', () => {
112112
};
113113
});
114114

115+
test('unowned deriveds are not added as reactions but trigger effects', () => {
116+
var obj = state<any>(undefined);
117+
118+
class C1 {
119+
#v = state(0);
120+
get v() {
121+
return $.get(this.#v);
122+
}
123+
set v(v: number) {
124+
set(this.#v, v);
125+
}
126+
}
127+
128+
return () => {
129+
let d = derived(() => $.get(obj)?.v || '-');
130+
131+
const log: number[] = [];
132+
assert.equal($.get(d), '-');
133+
134+
let destroy = effect_root(() => {
135+
render_effect(() => {
136+
log.push($.get(d));
137+
});
138+
});
139+
140+
set(obj, new C1());
141+
flushSync();
142+
assert.equal($.get(d), '-');
143+
$.get(obj).v = 1;
144+
flushSync();
145+
assert.equal($.get(d), 1);
146+
assert.deepEqual(log, ['-', 1]);
147+
destroy();
148+
// ensure we're not leaking reactions
149+
assert.equal(obj.reactions, null);
150+
assert.equal(d.reactions, null);
151+
};
152+
});
153+
115154
test('derived from state', () => {
116155
const log: number[] = [];
117156

0 commit comments

Comments
 (0)