Skip to content

Commit

Permalink
Merge pull request #512 from jviide/patch-01
Browse files Browse the repository at this point in the history
Fix: always reset the evaluation context upon entering an untracked block
  • Loading branch information
marvinhagemeister committed Feb 28, 2024
2 parents 34c8752 + e357b4d commit d7f2afa
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 53 deletions.
7 changes: 0 additions & 7 deletions packages/core/src/index.ts
Expand Up @@ -99,19 +99,12 @@ function batch<T>(callback: () => T): T {
// Currently evaluated computed or effect.
let evalContext: Computed | Effect | undefined = undefined;

let untrackedDepth = 0;

function untracked<T>(callback: () => T): T {
if (untrackedDepth > 0) {
return callback();
}
const prevContext = evalContext;
evalContext = undefined;
untrackedDepth++;
try {
return callback();
} finally {
untrackedDepth--;
evalContext = prevContext;
}
}
Expand Down
106 changes: 60 additions & 46 deletions packages/core/test/signal.test.tsx
Expand Up @@ -659,36 +659,6 @@ describe("effect()", () => {
expect(spy).not.to.be.called;
});

it("should not run if readed signals in a untracked", () => {
const a = signal(1);
const b = signal(2);
const spy = sinon.spy(() => a.value + b.value);
effect(() => untracked(spy));
a.value = 10;
b.value = 20;

expect(spy).to.be.calledOnce;
});

it("should not throw on assignment in untracked", () => {
const a = signal(1);
const aChangedTime = signal(0);

const dispose = effect(() => {
a.value;
untracked(() => {
aChangedTime.value = aChangedTime.value + 1;
});
});

expect(() => (a.value = 2)).not.to.throw();
expect(aChangedTime.value).to.equal(2);
a.value = 3;
expect(aChangedTime.value).to.equal(3);

dispose();
});

it("should not rerun parent effect if a nested child effect's signal's value changes", () => {
const parentSignal = signal(0);
const childSignal = signal(0);
Expand Down Expand Up @@ -995,22 +965,6 @@ describe("computed()", () => {
expect(spy).to.be.calledTwice;
});

it("should not recompute if readed signals in a untracked", () => {
const a = signal(1);
const b = signal(2);
const spy = sinon.spy(() => a.value + b.value);
const c = computed(() => untracked(spy));

expect(spy).to.not.be.called;
expect(c.value).to.equal(3);
a.value = 10;
c.value;
b.value = 20;
c.value;
expect(spy).to.be.calledOnce;
expect(c.value).to.equal(3);
});

it("should store thrown non-errors and recompute only after a dependency changes", () => {
const a = signal(0);
const spy = sinon.spy();
Expand Down Expand Up @@ -1946,3 +1900,63 @@ describe("batch/transaction", () => {
expect(callCount).to.equal(1);
});
});

describe("untracked", () => {
it("should block tracking inside effects", () => {
const a = signal(1);
const b = signal(2);
const spy = sinon.spy(() => a.value + b.value);
effect(() => untracked(spy));
expect(spy).to.be.calledOnce;

a.value = 10;
b.value = 20;
expect(spy).to.be.calledOnce;
});

it("should block tracking even when run inside effect run inside untracked", () => {
const s = signal(1);
const spy = sinon.spy(() => s.value);

untracked(() => effect(() => untracked(spy)));
expect(spy).to.be.calledOnce;

s.value = 2;
expect(spy).to.be.calledOnce;
});

it("should not cause signal assignments throw", () => {
const a = signal(1);
const aChangedTime = signal(0);

const dispose = effect(() => {
a.value;
untracked(() => {
aChangedTime.value = aChangedTime.value + 1;
});
});

expect(() => (a.value = 2)).not.to.throw();
expect(aChangedTime.value).to.equal(2);
a.value = 3;
expect(aChangedTime.value).to.equal(3);

dispose();
});

it("should block tracking inside computed signals", () => {
const a = signal(1);
const b = signal(2);
const spy = sinon.spy(() => a.value + b.value);
const c = computed(() => untracked(spy));

expect(spy).to.not.be.called;
expect(c.value).to.equal(3);
a.value = 10;
c.value;
b.value = 20;
c.value;
expect(spy).to.be.calledOnce;
expect(c.value).to.equal(3);
});
});

0 comments on commit d7f2afa

Please sign in to comment.