Skip to content
Guide/Reactivity

Reactivity

The core ships no reactive primitive. QubeeStore exposes two methods, and every framework binds to them differently:

store.getSnapshot(); // the current state — stable identity between writes
store.subscribe(fn); // returns an unsubscribe function

That is deliberately React’s useSyncExternalStore contract, which also happens to be the smallest sensible one.

import { useSyncExternalStore } from 'react';
function usePage(store: QubeeStore) {
return useSyncExternalStore(store.subscribe, store.getSnapshot);
}

getSnapshot() returns the same object until the next write, which is what stops useSyncExternalStore re-rendering forever.

private readonly _state = signal(this.store.getSnapshot());
constructor() {
this.store.subscribe(() => this._state.set(this.store.getSnapshot()));
}
readonly currentPage = computed(() => this._state().page);
const state = shallowRef(store.getSnapshot());
store.subscribe(() => (state.value = store.getSnapshot()));
const stop = store.subscribe(() => console.log(qb.generateUri()));
stop();

The returned state is deeply frozen. Mutating it throws in strict mode rather than silently corrupting a later read:

store.getSnapshot().includes.push('author');
// TypeError: Cannot add property 0, object is not extensible

Two guarantees have to hold at once — a stable identity between writes, and immunity from caller mutation. Freezing is what makes both true.