Skip to content

Commit fbabdbd

Browse files
committed
fix: outer effect keeps responding to own dep after inner re-runs
When an inner effect re-runs on its own (its own dep changed), notify() walks up the parent chain to add ancestors to the queue and clears their Watching flag to prevent duplicate notification. The parent then runs but isn't dirty/pending, so run() takes the "not dirty" branch. Before #109/#110, that branch unconditionally restored Watching. The fix in #109/#110 added `if (e.flags)` to avoid restoring Watching on disposed effects (whose flags were zeroed during checkDirty's traversal). Unfortunately, parents added to the queue via the chain also have flags === 0 (notify just cleared Watching, no other flag set), so they fell into the same branch and lost Watching permanently — the outer effect stopped responding to its own dep after any inner re-run. Disposed effects also have deps === undefined (purgeDeps clears them). Use that as the more precise discriminator: restore Watching when the effect still has deps, skip when fully disposed. Fixes #115. Added a regression test mirroring the report.
1 parent 0775f82 commit fbabdbd

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ function run(e: EffectNode): void {
269269
e.flags &= ~ReactiveFlags.RecursedCheck;
270270
purgeDeps(e);
271271
}
272-
} else if (e.flags) {
272+
} else if (e.deps !== undefined) {
273273
e.flags = ReactiveFlags.Watching;
274274
}
275275
}

tests/effect.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,29 @@ test('should support custom recurse effect', () => {
1515

1616
expect(triggers).toBe(6);
1717
});
18+
19+
// https://github.com/stackblitz/alien-signals/issues/115
20+
test('outer effect keeps responding to its own dep after inner re-runs', () => {
21+
const a = signal(0);
22+
const b = signal(0);
23+
let outerRuns = 0;
24+
let innerRuns = 0;
25+
26+
effect(() => {
27+
a();
28+
outerRuns++;
29+
effect(() => {
30+
b();
31+
innerRuns++;
32+
});
33+
});
34+
expect(outerRuns).toBe(1);
35+
expect(innerRuns).toBe(1);
36+
37+
b(1);
38+
expect(outerRuns).toBe(1);
39+
expect(innerRuns).toBeGreaterThanOrEqual(2);
40+
41+
a(1);
42+
expect(outerRuns).toBe(2);
43+
});

0 commit comments

Comments
 (0)