Skip to content

Commit

Permalink
Error on signal writes inside read callbacks.
Browse files Browse the repository at this point in the history
Adds more tests.
  • Loading branch information
rkirov committed Feb 24, 2023
1 parent 9153739 commit c90a636
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 6 deletions.
12 changes: 12 additions & 0 deletions basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ test('basic signal', () => {
expect(double.value).toBe(12);
});

test('basic pass-through', () => {
const a = input(1);
const b = a.read(x => x);
expect(b.value).toBe(1);
});

test('basic return signal', () => {
const a = input(1);
const b = a.read(_ => a);
expect(b.value).toBe(1);
});

test('nested readers', () => {
const a = input(1);
const b = input(2);
Expand Down
13 changes: 13 additions & 0 deletions errors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {input} from '.';

test('errors on non-reactive reads in read callbacks', () => {
const a = input(1);
const d = a.read(_ => a.value * 2);
expect(() => d.value).toThrow();
});

test('errors on non-reactive writes in read callbacks', () => {
const a = input(1);
const d = a.read(_ => { a.value = 2; return a;});
expect(() => d.value).toThrow();
});
17 changes: 11 additions & 6 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@ class SignalImpl<T> implements Signal<T> {
return `Signal('${this.name}', ${this.id})`;
}
get value(): T {
if (globalState === GLOBAL_STATE.COMPUTING) {
throw new Error(`error: non-reactive read of signal ${this}. Please use .read()`);
}
this.checkGlobalState();
if (this.state !== State.DIRTY) return this.#cachedValue;
// during recomputation the readers can change, so we remove them first.
// TODO: use counters trick to optimize this.
Expand All @@ -139,6 +137,14 @@ class SignalImpl<T> implements Signal<T> {
for (let i of this.inputs) i.readers.add(this.#ref);
return this.#cachedValue;
}

protected checkGlobalState() {
if (globalState === GLOBAL_STATE.COMPUTING) {
// reset global state before throwing.
globalState = GLOBAL_STATE.READY;
throw new Error(`error: non-reactive read of signal ${this}. Please use .read()`);
}
}
}

class InputImpl<T> extends SignalImpl<T> {
Expand All @@ -150,12 +156,11 @@ class InputImpl<T> extends SignalImpl<T> {
super(_ => {throw new Error(`error: inputs continuation shouldn't be called`)}, name);
}
get value(): T {
if (globalState === GLOBAL_STATE.COMPUTING) {
throw new Error(`error: non-reactive read of signal ${this}. Please use .read()`);
}
this.checkGlobalState();
return this.val;
}
set value(t: T) {
this.checkGlobalState();
if (this.eq(this.val, t)) return;
this.val = t;
for (let r of this.readers) {
Expand Down

0 comments on commit c90a636

Please sign in to comment.