@mongez/react-atom
Version:
A simple state management tool for React Js.
1 lines • 5.04 kB
Source Map (JSON)
{"version":3,"file":"store.mjs","names":[],"sources":["../../../../@mongez/react-atom/src/store.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n AtomStore,\n type Atom,\n createAtomStore,\n} from \"@mongez/atom\";\nimport React, { createContext, useContext, useEffect, useState } from \"react\";\n\n/**\n * React context that holds the active atom store. Components that read or\n * write atoms via the React hooks resolve the store-scoped clone from this\n * context. When the context is null (no provider mounted), hooks fall back\n * to the module-level singleton atom, which is the right behavior for a\n * client-only SPA.\n */\nexport const AtomStoreContext = createContext<AtomStore | null>(null);\n\nexport type AtomStoreProviderProps = {\n /**\n * An existing store to use. If omitted, the provider creates its own\n * fresh store on first render. Pass an externally created store when you\n * need to mutate it outside React (e.g. during Next.js data loading).\n */\n store?: AtomStore;\n\n /**\n * Atom templates to pre-register in the store. Pre-registration is\n * required when you want the initial values from `initialValues` to apply\n * to atoms that have not yet been used in React.\n */\n initialAtoms?: Atom<any>[];\n\n /**\n * Initial values keyed by atom key. Applied silently (no update event)\n * so the first render of subscribers sees the hydrated value.\n *\n * If an atom in this map has not been pre-registered via `initialAtoms`\n * or used yet, its value is queued and applied the first time that atom\n * enters the store via a React hook.\n */\n initialValues?: Record<string, unknown>;\n\n children: React.ReactNode;\n};\n\n/**\n * Provider that scopes atom reads and writes to a request-local `AtomStore`.\n *\n * Wrap the root of your component tree (or any subtree) with this provider\n * to give that subtree its own isolated copy of every atom's state. This is\n * the supported pattern for SSR in Next.js, Remix, and TanStack Start —\n * each request creates its own store, so concurrent requests cannot see\n * each other's state.\n *\n * Without a provider, atoms fall back to the module-level singleton (the\n * historical client-only behavior).\n */\nexport function AtomStoreProvider({\n store,\n initialAtoms,\n initialValues,\n children,\n}: AtomStoreProviderProps) {\n const [activeStore] = useState<AtomStore>(() => {\n const next = store ?? createAtomStore();\n\n if (initialAtoms) {\n for (const atomTemplate of initialAtoms) {\n next.use(atomTemplate);\n }\n }\n\n if (initialValues) {\n next.hydrate(initialValues);\n }\n\n return next;\n });\n\n useEffect(() => {\n return () => {\n // Only auto-destroy stores that the provider itself created. Stores\n // passed in via props are owned by the caller.\n if (!store) {\n activeStore.destroy();\n }\n };\n }, [activeStore, store]);\n\n return (\n <AtomStoreContext.Provider value={activeStore}>\n {children}\n </AtomStoreContext.Provider>\n );\n}\n\n/**\n * Read the active atom store. Returns null when no `<AtomStoreProvider>` is\n * mounted in the tree above this component.\n */\nexport function useAtomStore(): AtomStore | null {\n return useContext(AtomStoreContext);\n}\n\n/**\n * Resolve an atom for the current render context.\n *\n * Two call shapes:\n *\n * - `useAtom(template)` — pass an atom you imported. Returns the\n * store-scoped clone if a `<AtomStoreProvider>` is mounted above; falls\n * back to the template itself otherwise. Use this when you need to call\n * action methods (`startLoading()`, `open()`, etc.) from event handlers\n * in an SSR-safe way.\n *\n * - `useAtom(key)` — pass a string key. Returns the scoped atom registered\n * under that key in the active store, or `undefined` when no provider is\n * mounted or the key has not entered the store. This is a legacy escape\n * hatch; prefer the template form.\n */\nexport function useAtom<V, A extends Record<string, any> = {}>(\n template: Atom<V, A>\n): Atom<V, A>;\nexport function useAtom<V = any>(key: string): Atom<V> | undefined;\nexport function useAtom(arg: Atom<any> | string): Atom<any> | undefined {\n const store = useContext(AtomStoreContext);\n if (typeof arg === \"string\") {\n return store?.get(arg);\n }\n return store ? store.use(arg) : arg;\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,MAAa,mBAAmB,cAAgC,IAAI;;;;;;;;;;;;;AA0CpE,SAAgB,kBAAkB,EAChC,OACA,cACA,eACA,YACyB;CACzB,MAAM,CAAC,eAAe,eAA0B;EAC9C,MAAM,OAAO,SAAS,gBAAgB;EAEtC,IAAI,cACF,KAAK,MAAM,gBAAgB,cACzB,KAAK,IAAI,YAAY;EAIzB,IAAI,eACF,KAAK,QAAQ,aAAa;EAG5B,OAAO;CACT,CAAC;CAED,gBAAgB;EACd,aAAa;GAGX,IAAI,CAAC,OACH,YAAY,QAAQ;EAExB;CACF,GAAG,CAAC,aAAa,KAAK,CAAC;CAEvB,OACE,oBAAC,iBAAiB,UAAlB;EAA2B,OAAO;EAC/B;CACwB;AAE/B;;;;;AAMA,SAAgB,eAAiC;CAC/C,OAAO,WAAW,gBAAgB;AACpC;AAsBA,SAAgB,QAAQ,KAAgD;CACtE,MAAM,QAAQ,WAAW,gBAAgB;CACzC,IAAI,OAAO,QAAQ,UACjB,OAAO,OAAO,IAAI,GAAG;CAEvB,OAAO,QAAQ,MAAM,IAAI,GAAG,IAAI;AAClC"}