Rxova
Skip to content

SharedStore

Type Parameter
S extends Record<string, unknown>
readonly clientId: string;

readonly hydrated: Promise<void>;

Resolves once persisted state has been restored — or refused, or found absent. Already resolved when there is no persist option at all.

Exists because an async adapter cannot hydrate before the store is handed back, and until now that gap was unobservable: a keystroke landing in it writes at counter 1, the restore arrives holding counter 5, and last-writer-wins correctly discards the newer keystroke. The behaviour is right and the surprise is total. Gate first paint or first input on this and the gap closes:

await store.hydrated;

Never rejects. A refused restore is reported through persist.onRestoreError and still settles, because a store that kept its initial values is usable and a promise nobody can await is not.


readonly state: S;

Live proxy for imperative use: store.state.count++ syncs everywhere.

close(): void;

void


getSnapshot(): Readonly<S>;

Immutable snapshot, replaced whenever a change is applied. Safe for useSyncExternalStore.

Readonly<S>


getVersions(): Readonly<Record<string, Version>>;

The per-key version clocks behind the snapshot. Referentially stable, like getSnapshot.

Readonly<Record<string, Version>>


registerKey<K>(key, initial): void;

Register a key lazily at version [0, clientId] — any patch or snapshot a peer has already made for it wins over the initial value. No-op if the key already exists.

Type Parameter
K extends string
ParameterType
keyK
initialS[K]

void


set<K>(key, value): void;
Type Parameter
K extends string
ParameterType
keyK
valueS[K] | ((prev) => S[K])

void


subscribe(fn): () => void;
ParameterType
fn(key, value, meta) => void

() => void


subscribeKey(key, fn): () => void;
ParameterType
keykeyof S & string
fn() => void

() => void


transaction<T>(fn): T;

Apply several writes, then notify subscribers once with the settled state.

store.transaction(() => {
store.set('firstName', 'Ada');
store.set('lastName', 'Lovelace');
});

Local batching, not a distributed transaction. Each write is still its own patch on the wire, so a peer may see them arrive separately — making them atomic across tabs would need a wire type older builds would silently ignore, which is worse than the problem. What this buys is one re-render instead of N, and subscribers that never observe a half-applied group.

Nests: only the outermost call flushes. Returns whatever fn returns, and flushes even if fn throws — the writes that did land are already real.

Type Parameter
T
ParameterType
fn() => T

T