Skip to content
Primitives

createStore

Lazily-initialized store with StoreProvider, effectScope lifecycle, inter-store dependencies, and Redux DevTools action tracking in development.

Tasks
No tasks yet. Add your first task above!
import {
import createStore
createStore
,
import observable
observable
,
import StoreProvider
StoreProvider
} from "@usels/web";
const [
const useCountStore: any
useCountStore
] =
import createStore
createStore
("count", () => {
const
const count$: any
count$
=
import observable
observable
(0);
const
const increment: () => any
increment
= () =>
const count$: any
count$
.
any
set
((
v: any
v
) =>
v: any
v
+ 1);
return {
count$: any
count$
,
increment: () => any
increment
};
});
function
function App(): JSX.Element
App
() {
return (
<
import StoreProvider
StoreProvider
>
<
function Counter(): JSX.Element
Counter
/>
</
import StoreProvider
StoreProvider
>
);
}
function
function Counter(): JSX.Element
Counter
() {
const {
const count$: any
count$
,
const increment: any
increment
} =
const useCountStore: any
useCountStore
();
return <
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const increment: any
increment
}>{
const count$: any
count$
.
any
get
()}</
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>;
}

createStore returns a tuple of two functions:

  • useStore (tuple[0]) — React hook, call inside React components via useContext.
  • getStore (tuple[1]) — core accessor, call inside another store’s setup() or inside a useScope factory within a StoreProvider.
import { createStore } from "@usels/web";
const [useAuthStore, getAuthStore] = createStore("auth", () => {
const user$ = observable<string | null>(null);
const login = (name: string) => user$.set(name);
return { user$, login };
});
// useAuthStore() — call inside React components
// getAuthStore() — call inside another store's setup() or useScope factory

Use getStore (tuple[1]) inside another store’s setup() to declare inter-store dependencies.

import { createStore, observable } from "@usels/web";
const [, getAuthStore] = createStore("auth", () => {
const user$ = observable<string | null>(null);
return { user$ };
});
const [useCountStore] = createStore("count", () => {
const { user$ } = getAuthStore(); // inter-store dep
const count$ = observable(0);
return { count$, user$ };
});

getStore also works inside a useScope factory when the component is rendered within a StoreProvider. This allows store access in hook-level reactive scopes without prop drilling.

import { createStore } from "@usels/web";
import { useScope, observe } from "@usels/web/state/useScope";
const [, getSettingsStore] = createStore("settings", () => {
const theme$ = observable<"light" | "dark">("light");
return { theme$ };
});
function useThemeSync() {
return useScope(() => {
const { theme$ } = getSettingsStore(); // resolves from StoreProvider
observe(() => {
document.documentElement.dataset.theme = theme$.get();
});
return {};
});
}

getStore() throws if called outside a store setup() or outside a StoreProvider.

Returns the current StoreRegistryValue from the nearest StoreProvider, or null if not inside one. Does not throw — useful when you need optional store access.

import { useStoreRegistry } from "@usels/web";
function useOptionalStore() {
const registry = useStoreRegistry(); // null if no StoreProvider
if (!registry) return null;
// use registry...
}

Call onMount and onUnmount inside a store’s setup() to register lifecycle callbacks. The StoreProvider runs onMount when it mounts and disposes scopes (running onUnmount) when it unmounts.

import { createStore, observable } from "@usels/web";
import { onMount, onUnmount } from "@usels/web/state/useScope/effectScope";
const [useWebSocketStore] = createStore("ws", () => {
const messages$ = observable<string[]>([]);
let socket: WebSocket | null = null;
onMount(() => {
socket = new WebSocket("wss://example.com");
socket.onmessage = (e) => messages$.set((prev) => [...prev, e.data]);
return () => socket?.close();
});
return { messages$ };
});

onUnmount is shorthand for onMount(() => cb) when you only need cleanup:

onUnmount(() => {
resource.dispose();
});

Use observe from the scope-aware import so subscriptions are automatically cleaned up when the StoreProvider unmounts.

import { createStore, observable } from "@usels/web";
import { observe } from "@usels/web/state/useScope/observe";
const [useDocStore] = createStore("doc", () => {
const content$ = observable("");
const isDirty$ = observable(false);
observe(() => {
if (content$.get()) isDirty$.set(true);
});
return { content$, isDirty$ };
});

Stores auto-connect to Redux DevTools Extension in development mode. Pass dangerouslyUseInProduction to StoreProvider only when you explicitly want the same DevTools integration in production.

  • Action entries (storeName/actionName) — logged when an action function is called.
  • State entries (storeName/__state) — logged when observables change outside an action. Multiple synchronous changes are batched into one entry.
import { createStore, observable, StoreProvider } from "@usels/web";
const [useAppStore] = createStore("app", () => {
const theme$ = observable<"light" | "dark">("light");
const toggleTheme = () => theme$.set((v) => (v === "light" ? "dark" : "light"));
return { theme$, toggleTheme };
});
// DevTools timeline:
// @@INIT → { theme$: "light" }
// app/toggleTheme → { theme$: "dark" }
function App() {
return (
<StoreProvider dangerouslyUseInProduction>
<MainContent />
</StoreProvider>
);
}

StoreProvider is required. It isolates stores per provider instance for SSR safety. Stores are lazily initialized on first hook call and cached within the provider. The provider is responsible for executing onMount and onUnmount lifecycle callbacks.

import { createStore, StoreProvider } from "@usels/web";
function App() {
return (
<StoreProvider>
<MainContent />
</StoreProvider>
);
}

View on GitHub