Tiny reactive primitives — the reactive core of aihu.
Small (≤ 2 kB gz) reactive primitives — the foundation layer for aihu’s arbor renderer. One read shape (the signal tuple), one underlying cell. Sync semantics, lazy computed, explicit batch. No proxies, no scheduler queue, no global tick.
signalfunction signal<T>(initial: T, options?: SignalOptions<T>): Signal<T>Create a reactive cell. Returns a readonly [read, write] tuple — the single read shape the whole runtime is built on.
| Parameter | Type | Description |
|---|---|---|
initial | T | The initial value held by the cell. |
options? | SignalOptions<T> | Optional equals comparator to suppress no-op notifications. |
ReturnsSignal<T> — a readonly [Read<T>, Write<T>] tuple.
import { signal, effect } from '@aihu/signals'
const [count, setCount] = signal(0)
effect(() => console.log('count =', count()))
setCount(1) // logs "count = 1"
setCount((n) => n + 10) // logs "count = 11"computedfunction computed<T>(fn: () => T, options?: ComputedOptions<T>): Read<T> & { dispose(): void }A lazily-evaluated derived value. Recomputes only when read after a dependency changed; memoized by Object.is (or a custom equals).
| Parameter | Type | Description |
|---|---|---|
fn | () => T | Pure computation over other reactive reads. |
options? | ComputedOptions<T> | Optional equals comparator for memoization. |
ReturnsRead<T> & { dispose(): void }
import { signal, computed } from '@aihu/signals'
const [n, setN] = signal(2)
const doubled = computed(() => n() * 2)
doubled() // 4effectfunction effect(fn: EffectFn): DisposeRun a side effect and re-run it whenever any signal read inside it changes. Returns a Dispose to stop tracking.
| Parameter | Type | Description |
|---|---|---|
fn | EffectFn | The reactive side effect to run and track. |
ReturnsDispose — call to tear the effect down.
import { signal, effect } from '@aihu/signals'
const [ok, setOk] = signal(true)
const stop = effect(() => document.title = ok() ? 'on' : 'off')
stop() // stop reactingbatchfunction batch<T>(fn: () => T): TCoalesce multiple writes so dependents recompute once, after the batch commits — the explicit alternative to a global scheduler tick.
| Parameter | Type | Description |
|---|---|---|
fn | () => T | Work that performs several writes. |
ReturnsT — the value returned by fn.
import { signal, batch, effect } from '@aihu/signals'
const [a, setA] = signal(1)
const [b, setB] = signal(2)
effect(() => console.log(a() + b()))
batch(() => { setA(10); setB(20) }) // logs once: 30untrackfunction untrack<T>(fn: () => T): TRead signals without subscribing the current effect/computed to them.
| Parameter | Type | Description |
|---|---|---|
fn | () => T | A read that must not create a dependency. |
ReturnsT — the value returned by fn.
Signaltype Signal<T> = readonly [Read<T>, Write<T>]The tuple returned by signal — a reader and a writer over one cell.
Readtype Read<T> = () => TA tracked getter. Calling it inside an effect/computed subscribes to the cell.
Writetype Write<T> = <U extends T>(next: (Exclude<U, AnyFn> & T) | ((prev: T) => U)) => voidA setter accepting either a next value or an updater function of the previous value.