A signal is a single reactive value: you read and write it through one function handle, and anything that read it re-runs automatically the moment you write it. This is the core of fine-grained reactivity libraries like SolidJS, Preact Signals, and Vue's ref/computed. You will build three primitives that work together — signal for state, computed for derived values, and effect for side effects — wired by automatic dependency tracking, so callers never declare a dependency list by hand. The key mechanism is a global stack of the observers currently running: any signal read during a run subscribes whoever is on top.
type Read<T> = () => T;
type Signal<T> = {
(): T; // read (and subscribe the running observer)
(next: T): T; // write (and re-run every observer that read it)
};
const signalsComputedEffect: {
signal<T>(initial: T): Signal<T>; // read/write state handle
computed<T>(fn: () => T): Read<T>; // lazy, cached derived value
effect(fn: () => void): () => void; // runs now, re-runs on change; returns dispose()
};
const { signal, computed, effect } = signalsComputedEffect;
const count = signal(1);
const double = computed(() => count() * 2);
effect(() => console.log('double is', double())); // logs: double is 2
count(5); // logs: double is 10
count(5); // nothing logs — the value did not change
const a = signal(0);
const b = signal(0);
const stop = effect(() => console.log(a())); // logs 0; reads a, never b
b(99); // nothing logs — the effect never read b
a(1); // logs 1
stop(); // dispose the effect
a(2); // nothing logs — the effect is disposed
computed or effect subscribes it automatically; there is no dependency array to pass.computed recomputes only when it is read after one of its sources changed; between changes it returns the cached value, and the derived function does not run.if is tracked only on the runs where it actually happens.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.