Skip to content

createStore

createStore creates a BehaviorSubject-based reactive state container. It provides a familiar get/set API while exposing the current state as an Observable stream. Selectors derived via select() emit only when the selected slice actually changes, keeping downstream subscribers efficient.

import { createStore } from "rxjs-ai";
import { createStore } from "rxjs-ai";
interface CounterState {
count: number;
label: string;
}
const store = createStore<CounterState>({
count: 0,
label: "My Counter",
});
// Subscribe to the full state stream
store.state$.subscribe((state) => {
console.log("State changed:", state);
});
// Read synchronously
console.log(store.getState().count); // 0
// Derive a selector – only emits when `count` changes
const count$ = store.select((s) => s.count);
count$.subscribe((count) => console.log("count:", count));
// Patch a partial update
store.patchState({ count: 1 });
// Replace the entire state with a function-based updater
store.setState((prev) => ({ ...prev, count: prev.count + 1 }));
// Clean up when done
store.destroy();
MemberSignatureDescription
state$Observable<State>Emits the full state object on every change. Replays the current value to new subscribers.
getState()() => StateReturns a synchronous snapshot of the current state.
setState(updater)(updater: State | StateUpdater<State>) => voidReplaces the entire state. Accepts a new state object or a callback that receives the previous state and returns the next state.
patchState(patch)(patch: StatePatch<State>) => voidShallow-merges a partial object into the current state. Accepts a partial object or a function that receives the previous state and returns a partial object.
select(selector, compare?)<R>(selector: (state: State) => R, compare?: (a: R, b: R) => boolean) => Observable<R>Derives a projected Observable from the state. Uses distinctUntilChanged so subscribers only receive emissions when the selected value changes.
destroy()() => voidCompletes the internal BehaviorSubject and all derived selectors.

By default, select uses referential equality (===) to determine whether the derived value has changed. You can supply a custom comparator for deep or structural equality checks:

import { isEqual } from "lodash-es";
const todos$ = store.select((s) => s.todos, isEqual);

This is especially useful when the selector returns a new array or object reference on every emission but the contents remain the same.

setState accepts either a plain state object or an updater function. Updater functions are the safest choice when the next state depends on the previous state:

// Plain object – replaces the entire state
store.setState({ count: 10, label: "Reset" });
// Updater function – derives next state from previous
store.setState((prev) => ({
...prev,
count: prev.count + 1,
}));
/** The store instance returned by createStore. */
type Store<State> = {
state$: Observable<State>;
getState: () => State;
setState: (updater: State | StateUpdater<State>) => void;
patchState: (patch: StatePatch<State>) => void;
select: <R>(
selector: (state: State) => R,
compare?: (a: R, b: R) => boolean,
) => Observable<R>;
destroy: () => void;
};
/** A callback that receives the current state and returns the next state. */
type StateUpdater<State> = (current: State) => State;
/** A partial representation of the state used by patchState. Accepts a partial object or a function that returns one. */
type StatePatch<State extends object> =
| Partial<State>
| ((previousState: State) => Partial<State>);