createStore
Lazily-initialized store with StoreProvider, effectScope lifecycle, inter-store dependencies, and Redux DevTools action tracking in development.
Basic store
Section titled “Basic store”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>;}Tuple return: [useStore, getStore]
Section titled “Tuple return: [useStore, getStore]”createStore returns a tuple of two functions:
useStore(tuple[0]) — React hook, call inside React components viauseContext.getStore(tuple[1]) — core accessor, call inside another store’ssetup()or inside auseScopefactory within aStoreProvider.
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 factoryInter-store dependency
Section titled “Inter-store dependency”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() inside useScope
Section titled “getStore() inside useScope”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.
useStoreRegistry()
Section titled “useStoreRegistry()”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...}Lifecycle: onMount and onUnmount
Section titled “Lifecycle: onMount and onUnmount”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();});Reactive subscriptions with observe
Section titled “Reactive subscriptions with observe”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$ };});Redux DevTools integration
Section titled “Redux DevTools integration”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
Section titled “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> );}