Reactive
The reactive module provides fine‑grained reactivity with minimal primitives.
ts
import {
signal,
computed,
effect,
batch,
watch,
readonly,
untrack,
isSignal,
isComputed,
} from '@bquery/bquery/reactive';
const count = signal(0);
const doubled = computed(() => count.value * 2);
effect(() => {
console.log('Count changed', count.value);
});
// Watch with value comparison
watch(count, (newVal, oldVal) => {
console.log(`Changed from ${oldVal} to ${newVal}`);
});
batch(() => {
count.value++;
count.value++;
});Signal
ts
const name = signal('World');
name.value = 'bQuery';Signal API
value(getter/setter) – tracked reads, reactive writespeek()– read without trackingupdate(updater)– update based on current value
Computed
Computed values are lazy and cached until dependencies change.
ts
const total = computed(() => price.value * quantity.value);Computed API
value(getter) – recomputes when dependencies change
Effect
Effects run immediately and re-run when any accessed signal/computed changes. They can return a cleanup function.
ts
const stop = effect(() => {
document.title = `Count: ${count.value}`;
return () => console.log('cleanup');
});
stop();Batch
Batch groups multiple updates into one notification pass.
ts
batch(() => {
count.value = 1;
count.value = 2;
});Persisted signals
persistedSignal syncs a signal to localStorage.
ts
import { persistedSignal } from '@bquery/bquery/reactive';
const theme = persistedSignal('theme', 'light');
theme.value = 'dark';Watch
Watch observes a signal and calls a callback with old and new values:
ts
import { watch } from '@bquery/bquery/reactive';
const count = signal(0);
const stop = watch(count, (newVal, oldVal) => {
console.log(`Changed: ${oldVal} → ${newVal}`);
});
count.value = 5; // logs: "Changed: 0 → 5"
stop(); // Stop watchingReadonly
Create a read-only view of a signal:
ts
import { readonly } from '@bquery/bquery/reactive';
const count = signal(0);
const readOnlyCount = readonly(count);
console.log(readOnlyCount.value); // 0
// readOnlyCount.value = 1; // TypeScript error!Untrack
Read signals without creating dependencies:
ts
import { untrack } from '@bquery/bquery/reactive';
effect(() => {
// This will NOT re-run when `other` changes
const val = untrack(() => other.value);
console.log(count.value, val);
});Type Guards
Check if a value is a signal or computed:
ts
import { isSignal, isComputed } from '@bquery/bquery/reactive';
const count = signal(0);
const doubled = computed(() => count.value * 2);
isSignal(count); // true
isSignal(doubled); // false
isComputed(doubled); // true
isComputed(count); // false