Signals
Signals are Whisq’s reactive primitives. They hold values that automatically track dependencies and trigger updates when changed.
signal() — Reactive State
Section titled “signal() — Reactive State”import { signal } from "@whisq/core";
const count = signal(0);
count.value; // 0 — read (triggers dependency tracking)count.value = 5; // write (triggers updates)count.update(n => n + 1); // 6 — update via functioncount.peek(); // 6 — read WITHOUT trackingcount.set(10); // 10 — alias for direct assignmentWhen to use peek()
Section titled “When to use peek()”Use peek() inside effects when you need to read a value without creating a dependency:
effect(() => { // Re-runs when `trigger` changes, but NOT when `config` changes console.log(trigger.value, config.peek());});computed() — Derived Values
Section titled “computed() — Derived Values”computed() creates a read-only signal that auto-updates when its dependencies change:
import { signal, computed } from "@whisq/core";
const firstName = signal("Ada");const lastName = signal("Lovelace");const fullName = computed(() => `${firstName.value} ${lastName.value}`);
fullName.value; // "Ada Lovelace"
firstName.value = "Grace";fullName.value; // "Grace Lovelace" — auto-updatedComputed values are lazy — they don’t recompute until read. They also cache — reading twice without dependency changes returns the cached value.
effect() — Side Effects
Section titled “effect() — Side Effects”effect() runs a function immediately and re-runs it whenever its dependencies change:
import { signal, effect } from "@whisq/core";
const count = signal(0);
const dispose = effect(() => { console.log(`Count is: ${count.value}`);});// Logs: "Count is: 0"
count.value = 1;// Logs: "Count is: 1"
dispose(); // Stop watchingcount.value = 2; // No log — effect is disposedCleanup Functions
Section titled “Cleanup Functions”Return a function from an effect to run cleanup before each re-execution:
effect(() => { const timer = setInterval(() => tick(), 1000); return () => clearInterval(timer); // cleanup});Conditional Dependencies
Section titled “Conditional Dependencies”Effects only track signals read in the current execution:
const flag = signal(true);const a = signal("A");const b = signal("B");
effect(() => { // When flag is true, tracks `a` only // When flag is false, tracks `b` only console.log(flag.value ? a.value : b.value);});batch() — Grouped Updates
Section titled “batch() — Grouped Updates”batch() defers effect re-runs until all updates complete:
import { signal, effect, batch } from "@whisq/core";
const x = signal(0);const y = signal(0);
effect(() => { console.log(`${x.value}, ${y.value}`);});// Logs: "0, 0"
batch(() => { x.value = 1; y.value = 2;});// Logs: "1, 2" — only ONCE, not twiceWithout batch(), the effect would run once for x and again for y.
subscribe() — Manual Subscriptions
Section titled “subscribe() — Manual Subscriptions”For integrating with external systems:
const count = signal(0);
const unsub = count.subscribe(value => { // Called immediately with current value, then on every change externalSystem.update(value);});
unsub(); // Stop subscribingNext Steps
Section titled “Next Steps”- Elements — Use signals in reactive UI elements
- Components — Encapsulate signals in components