fix: keep deriveds reactive after their original parent effect was destroyed (#17171)

Use case: Remote queries that are created on one screen, then are used again on another screen. Original parent effect is destroyed in that case but derived should still be reactive. It wasn't prior to this fix because inside `get` the `destroyed` variable would be true and so deps would not properly be recorded.

Fixes https://github.com/sveltejs/kit/issues/14814
pull/17168/head
Simon H 2 days ago committed by GitHub
parent 7fd2d8660f
commit 686720070b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: keep deriveds reactive after their original parent effect was destroyed

@ -11,7 +11,8 @@ import {
STALE_REACTION,
ASYNC,
WAS_MARKED,
CONNECTED
CONNECTED,
DESTROYED
} from '#client/constants';
import {
active_reaction,
@ -296,7 +297,9 @@ function get_derived_parent_effect(derived) {
var parent = derived.parent;
while (parent !== null) {
if ((parent.f & DERIVED) === 0) {
return /** @type {Effect} */ (parent);
// The original parent effect might've been destroyed but the derived
// is used elsewhere now - do not return the destroyed effect in that case
return (parent.f & DESTROYED) === 0 ? /** @type {Effect} */ (parent) : null;
}
parent = parent.parent;
}

@ -1391,6 +1391,33 @@ describe('signals', () => {
};
});
test('derived whose original parent effect has been destroyed keeps updating', () => {
return () => {
let count: Source<number>;
let double: Derived<number>;
const destroy = effect_root(() => {
render_effect(() => {
count = state(0);
double = derived(() => $.get(count) * 2);
});
});
flushSync();
assert.equal($.get(double!), 0);
destroy();
flushSync();
set(count!, 1);
flushSync();
assert.equal($.get(double!), 2);
set(count!, 2);
flushSync();
assert.equal($.get(double!), 4);
};
});
test('$effect.root inside deriveds stay alive independently', () => {
const log: any[] = [];
const c = state(0);

Loading…
Cancel
Save