Skip to content

Commit d4cb4e4

Browse files
committed
fix: ensure derived effects reconnect after disconnection
Adds HAS_EFFECTS flag to track when deriveds contain side effects and re-runs computation when effects are missing after reconnection.
1 parent d2ba258 commit d4cb4e4

File tree

4 files changed

+60
-2
lines changed

4 files changed

+60
-2
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const REACTION_IS_UPDATING = 1 << 21;
2525
export const ASYNC = 1 << 22;
2626

2727
export const ERROR_VALUE = 1 << 23;
28+
export const HAS_EFFECTS = 1 << 24;
2829

2930
export const STATE_SYMBOL = Symbol('$state');
3031
export const LEGACY_PROPS = Symbol('legacy props');

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ import {
3333
EFFECT_PRESERVED,
3434
STALE_REACTION,
3535
USER_EFFECT,
36-
ASYNC
36+
ASYNC,
37+
HAS_EFFECTS
3738
} from '#client/constants';
3839
import * as e from '../errors.js';
3940
import { DEV } from 'esm-env';
@@ -156,6 +157,8 @@ function create_effect(type, fn, sync, push = true) {
156157
) {
157158
var derived = /** @type {Derived} */ (active_reaction);
158159
(derived.effects ??= []).push(effect);
160+
// Mark the derived as having effects
161+
derived.f |= HAS_EFFECTS;
159162
}
160163
}
161164

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
DISCONNECTED,
2121
REACTION_IS_UPDATING,
2222
STALE_REACTION,
23-
ERROR_VALUE
23+
ERROR_VALUE,
24+
HAS_EFFECTS
2425
} from './constants.js';
2526
import { old_values } from './reactivity/sources.js';
2627
import {
@@ -671,6 +672,13 @@ export function get(signal) {
671672

672673
if (is_dirty(derived)) {
673674
update_derived(derived);
675+
} else if (
676+
(derived.f & HAS_EFFECTS) !== 0 &&
677+
(derived.effects === null || derived.effects.length === 0)
678+
) {
679+
// If the derived once had effects but they're now missing (destroyed),
680+
// we need to re-execute to recreate them
681+
update_derived(derived);
674682
}
675683
}
676684

packages/svelte/tests/signals/test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,4 +1441,50 @@ describe('signals', () => {
14411441
assert.deepEqual(log, ['inner destroyed', 'inner destroyed']);
14421442
};
14431443
});
1444+
1445+
test('derived effects reconnect correctly', () => {
1446+
const log: string[] = [];
1447+
let a: Derived<number>;
1448+
1449+
return () => {
1450+
const destroy1 = effect_root(() => {
1451+
a = derived(() => {
1452+
user_effect(() => {
1453+
log.push('effect-executed');
1454+
});
1455+
return 42;
1456+
});
1457+
});
1458+
1459+
const destroy2 = effect_root(() => {
1460+
render_effect(() => {
1461+
$.get(a);
1462+
});
1463+
});
1464+
1465+
assert.equal(log.length, 0);
1466+
assert.equal(a?.effects?.length, 1);
1467+
1468+
destroy2();
1469+
flushSync();
1470+
1471+
assert.equal(a?.effects, null);
1472+
assert.equal(log.length, 0);
1473+
1474+
const destroy3 = effect_root(() => {
1475+
render_effect(() => {
1476+
const value = $.get(a);
1477+
assert.equal(value, 42);
1478+
});
1479+
});
1480+
1481+
flushSync();
1482+
1483+
assert.equal(log.length, 1);
1484+
assert.equal(a?.effects?.length, 1);
1485+
1486+
destroy3();
1487+
destroy1();
1488+
};
1489+
});
14441490
});

0 commit comments

Comments
 (0)