Skip to content

Commit 20bd595

Browse files
committed
Detect indirect effect cycles
1 parent 34dd5c3 commit 20bd595

File tree

2 files changed

+25
-8
lines changed

2 files changed

+25
-8
lines changed

packages/core/src/index.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,7 @@ function endBatch() {
6666
effect._nextBatchedEffect = undefined;
6767
effect._flags &= ~NOTIFIED;
6868

69-
if (
70-
!(effect._flags & DISPOSED) &&
71-
effect._flags & OUTDATED &&
72-
needsToRecompute(effect)
73-
) {
69+
if (!(effect._flags & DISPOSED) && needsToRecompute(effect)) {
7470
try {
7571
effect._callback();
7672
} catch (err) {
@@ -619,7 +615,7 @@ function Effect(this: Effect, compute: () => void) {
619615
this._cleanup = undefined;
620616
this._sources = undefined;
621617
this._nextBatchedEffect = undefined;
622-
this._flags = OUTDATED | TRACKING;
618+
this._flags = TRACKING;
623619
}
624620

625621
Effect.prototype._callback = function () {
@@ -643,15 +639,14 @@ Effect.prototype._start = function () {
643639
prepareSources(this);
644640

645641
/*@__INLINE__**/ startBatch();
646-
this._flags &= ~OUTDATED;
647642
const prevContext = evalContext;
648643
evalContext = this;
649644
return endEffect.bind(this, prevContext);
650645
};
651646

652647
Effect.prototype._notify = function () {
653648
if (!(this._flags & NOTIFIED)) {
654-
this._flags |= NOTIFIED | OUTDATED;
649+
this._flags |= NOTIFIED;
655650
this._nextBatchedEffect = batchedEffect;
656651
batchedEffect = this;
657652
}

packages/core/test/signal.test.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,28 @@ describe("effect()", () => {
524524
expect(fn).to.throw(/Cycle detected/);
525525
});
526526

527+
it("should throw on indirect cycles", () => {
528+
const a = signal(0);
529+
let i = 0;
530+
531+
const c = computed(() => {
532+
a.value;
533+
a.value = NaN;
534+
return NaN;
535+
});
536+
537+
const fn = () =>
538+
effect(() => {
539+
// Prevent test suite from spinning if limit is not hit
540+
if (i++ > 200) {
541+
throw new Error("test failed");
542+
}
543+
c.value;
544+
});
545+
546+
expect(fn).to.throw(/Cycle detected/);
547+
});
548+
527549
it("should allow disposing the effect multiple times", () => {
528550
const dispose = effect(() => undefined);
529551
dispose();

0 commit comments

Comments
 (0)