Skip to content

Commit 8148734

Browse files
authored
fix: ensure unowned deriveds correctly get re-linked to the graph (#14855)
* fix: ensure unowned deriveds correctly get re-linked to the graph * fix: ensure unowned deriveds correctly get re-linked to the graph * fix: ensure unowned deriveds correctly get re-linked to the graph * add test * add test * cleaner apporach * cleaner apporach * cleaner apporach * cleaner apporach
1 parent f7f87dc commit 8148734

File tree

3 files changed

+57
-19
lines changed

3 files changed

+57
-19
lines changed
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 correctly get re-linked to the graph

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

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -196,34 +196,34 @@ export function check_dirtiness(reaction) {
196196

197197
if (dependencies !== null) {
198198
var i;
199-
200-
if ((flags & DISCONNECTED) !== 0) {
201-
for (i = 0; i < dependencies.length; i++) {
202-
(dependencies[i].reactions ??= []).push(reaction);
199+
var dependency;
200+
var is_disconnected = (flags & DISCONNECTED) !== 0;
201+
var is_unowned_connected = is_unowned && active_effect !== null && !skip_reaction;
202+
var length = dependencies.length;
203+
204+
// If we are working with a disconnected or an unowned signal that is now connected (due to an active effect)
205+
// then we need to re-connect the reaction to the dependency
206+
if (is_disconnected || is_unowned_connected) {
207+
for (i = 0; i < length; i++) {
208+
dependency = dependencies[i];
209+
210+
if (!dependency?.reactions?.includes(reaction)) {
211+
(dependency.reactions ??= []).push(reaction);
212+
}
203213
}
204214

205-
reaction.f ^= DISCONNECTED;
215+
if (is_disconnected) {
216+
reaction.f ^= DISCONNECTED;
217+
}
206218
}
207219

208-
for (i = 0; i < dependencies.length; i++) {
209-
var dependency = dependencies[i];
220+
for (i = 0; i < length; i++) {
221+
dependency = dependencies[i];
210222

211223
if (check_dirtiness(/** @type {Derived} */ (dependency))) {
212224
update_derived(/** @type {Derived} */ (dependency));
213225
}
214226

215-
// If we are working with an unowned signal as part of an effect (due to !skip_reaction)
216-
// and the version hasn't changed, we still need to check that this reaction
217-
// is linked to the dependency source – otherwise future updates will not be caught.
218-
if (
219-
is_unowned &&
220-
active_effect !== null &&
221-
!skip_reaction &&
222-
!dependency?.reactions?.includes(reaction)
223-
) {
224-
(dependency.reactions ??= []).push(reaction);
225-
}
226-
227227
if (dependency.version > reaction.version) {
228228
return true;
229229
}

packages/svelte/tests/signals/test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,4 +781,37 @@ describe('signals', () => {
781781
assert.equal($.get(count), 0n);
782782
};
783783
});
784+
785+
test('unowned deriveds correctly re-attach to their source', () => {
786+
const log: any[] = [];
787+
788+
return () => {
789+
const a = state(0);
790+
const b = state(0);
791+
const c = derived(() => {
792+
$.get(a);
793+
return $.get(b);
794+
});
795+
796+
$.get(c);
797+
798+
set(a, 1);
799+
800+
const destroy = effect_root(() => {
801+
render_effect(() => {
802+
log.push($.get(c));
803+
});
804+
});
805+
806+
assert.deepEqual(log, [0]);
807+
808+
set(b, 1);
809+
810+
flushSync();
811+
812+
assert.deepEqual(log, [0, 1]);
813+
814+
destroy();
815+
};
816+
});
784817
});

0 commit comments

Comments
 (0)