@sigx/reactivity
v1.0.1
Published
Reactivity system for SignalX
Maintainers
Readme
@sigx/reactivity
Reactivity system for SignalX. Provides fine-grained reactive primitives including signals, computed values, effects, and watchers.
📚 Full guides, API reference and live examples → https://sigx.dev/core/packages/reactivity/overview/
Install
npm install @sigx/reactivityUsage
import { signal, computed, effect, batch } from '@sigx/reactivity';
const count = signal(0);
const doubled = computed(() => count.value * 2);
effect(() => {
console.log(`count: ${count.value}, doubled: ${doubled.value}`);
});
batch(() => {
count.value++;
count.value++;
});
// Logs once: "count: 2, doubled: 4"Reading a value tracks the key; enumerating also tracks the key set
Reading state.foo subscribes to that one key. Enumerating a reactive
object — Object.keys(), for…in, object spread, rest destructuring — adds a
subscription to its key set, so a key appearing or disappearing re-runs the
reader:
const state = signal<Record<string, number>>({ a: 1 });
effect(() => console.log(Object.keys(state)));
state.b = 2; // logs ['a', 'b'] — the key set changed
state.a = 99; // logs nothing — no key appeared or disappearedThe key-set subscription is additional, not a replacement. Object.keys()
reads no values, so it depends on the key set alone — but object spread and
rest destructuring copy each value out, so they subscribe per key as well and
do re-run when one of those values changes:
effect(() => console.log({ ...state }));
state.a = 99; // logs — the spread read `a`, so it depends on it'x' in state subscribes to the same per-key dependency a read of state.x
would, so presence and value always agree. Arrays subscribe through length,
which an index write already updates. Map and Set track iteration and
.size the same way.
Because each added key re-runs an enumerating reader, filling an object key by
key is quadratic — wrap bulk key changes in batch():
batch(() => { for (const [k, v] of entries) state[k] = v; }); // one re-runOne copy per page
The tracking context lives in this module, so an app must hold exactly one copy of it: signals written through a second copy never reach effects tracked by the first, silently. The package says so — a second copy evaluating in the same realm (two installed versions, a bundler that inlined one) throws in development with both versions and both module URLs, and warns once in production:
[sigx] Two copies of @sigx/reactivity are loaded: 0.15.0 at file:///…/.pnpm/…/dist/index.js and 1.0.0 at file:///…/@sigx/reactivity/dist/index.js.The fix is on the install side: the app installs sigx once and every library declares @sigx/* as a peer, so there is one copy to resolve to (docs/rfc-1.0.md §3). A lockfile with two @sigx/reactivity entries is the thing to look for. The stamp itself is globalThis.__SIGX_REACTIVITY__, registered in docs/seams.md.
📚 Documentation
The complete API reference (signal, isSignal, computed, effect, batch, watch, untrack, effectScope, onScopeDispose, toSignal, toSignals, and more), guides and live examples → https://sigx.dev/core/packages/reactivity/overview/
