diff --git a/examples/nextjs/CHANGELOG.md b/examples/nextjs/CHANGELOG.md index a3822100..4b086744 100644 --- a/examples/nextjs/CHANGELOG.md +++ b/examples/nextjs/CHANGELOG.md @@ -1,5 +1,13 @@ # nextjs-example +## 0.0.7 + +### Patch Changes + +- Updated dependencies + - r18gs@0.1.1 + - shared-ui@0.0.0 + ## 0.0.6 ### Patch Changes diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 53649462..fdaba98e 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -1,6 +1,6 @@ { "name": "nextjs-example", - "version": "0.0.6", + "version": "0.0.7", "private": true, "scripts": { "dev": "next dev", diff --git a/examples/remix/CHANGELOG.md b/examples/remix/CHANGELOG.md index 27807026..c56202dd 100644 --- a/examples/remix/CHANGELOG.md +++ b/examples/remix/CHANGELOG.md @@ -1,5 +1,13 @@ # remix-example +## 0.0.7 + +### Patch Changes + +- Updated dependencies + - r18gs@0.1.1 + - shared-ui@0.0.0 + ## 0.0.6 ### Patch Changes diff --git a/examples/remix/package.json b/examples/remix/package.json index 6043f6e3..82f6ead7 100644 --- a/examples/remix/package.json +++ b/examples/remix/package.json @@ -1,6 +1,6 @@ { "name": "remix-example", - "version": "0.0.6", + "version": "0.0.7", "private": true, "sideEffects": false, "type": "module", diff --git a/examples/vite/CHANGELOG.md b/examples/vite/CHANGELOG.md index b121cf9e..c85b9955 100644 --- a/examples/vite/CHANGELOG.md +++ b/examples/vite/CHANGELOG.md @@ -1,5 +1,13 @@ # vite-example +## 0.0.7 + +### Patch Changes + +- Updated dependencies + - r18gs@0.1.1 + - shared-ui@0.0.0 + ## 0.0.6 ### Patch Changes diff --git a/examples/vite/package.json b/examples/vite/package.json index 05b3b2d1..482e89ec 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -1,7 +1,7 @@ { "name": "vite-example", "private": true, - "version": "0.0.6", + "version": "0.0.7", "type": "module", "scripts": { "dev": "vite --port 3001", diff --git a/examples/vite/src/main.tsx b/examples/vite/src/main.tsx index 1f270783..684a45aa 100644 --- a/examples/vite/src/main.tsx +++ b/examples/vite/src/main.tsx @@ -3,7 +3,10 @@ import ReactDOM from "react-dom/client"; import App from "./App.tsx"; import { SharedRootLayout } from "shared-ui"; -ReactDOM.createRoot(document.getElementById("root")!).render( +const rootElement = document.getElementById("root"); +if (!rootElement) throw new Error("Root element not found"); + +ReactDOM.createRoot(rootElement).render( diff --git a/lib/r18gs/CHANGELOG.md b/lib/r18gs/CHANGELOG.md index 27071ee1..1e012731 100644 --- a/lib/r18gs/CHANGELOG.md +++ b/lib/r18gs/CHANGELOG.md @@ -1,5 +1,11 @@ # r18gs +## 0.1.1 + +### Patch Changes + +- Refactor without changing the exposed APIs. Some libraries using internal APIs may face temporary issues. + ## 0.1.0 ### Minor Changes diff --git a/lib/r18gs/package.json b/lib/r18gs/package.json index be52ef33..69dbfb73 100644 --- a/lib/r18gs/package.json +++ b/lib/r18gs/package.json @@ -2,7 +2,7 @@ "name": "r18gs", "author": "Mayank Kumar Chaudhari ", "private": false, - "version": "0.1.0", + "version": "0.1.1", "description": "A simple yet elegant, light weight, react18 global store to replace Zustand for better tree shaking.", "main": "./index.ts", "types": "./index.ts", diff --git a/lib/r18gs/src/use-rgs.ts b/lib/r18gs/src/use-rgs.ts index b7818137..82127b1a 100644 --- a/lib/r18gs/src/use-rgs.ts +++ b/lib/r18gs/src/use-rgs.ts @@ -1,27 +1,46 @@ /* eslint-disable @typescript-eslint/non-nullable-type-assertion-style -- as ! operator is forbidden by eslint*/ import { useSyncExternalStore } from "react"; -interface React18GlobalStore { - listeners: (() => void)[]; - value: unknown; -} - -export type SetterArgType = T | ((prevState: T) => T); - +type Listener = () => void; +type SetterArgType = T | ((prevState: T) => T); +type Subscriber = (l: Listener) => () => void; export type SetStateAction = (val: SetterArgType) => void; +/** + * This is a hack to reduce lib size + readability + not encouraging direct access to globalThis + */ +const [VALUE, LISTENERS, SETTER, SUBSCRIBER] = [0, 1, 2, 3]; +type RGS = [unknown, Listener[], SetStateAction, Subscriber]; + declare global { // eslint-disable-next-line no-var -- var required for global declaration. - var rgs: Record; - // eslint-disable-next-line no-var -- var required for global declaration. - var setters: Record | undefined>; - // eslint-disable-next-line no-var -- var required for global declaration. - var subscribers: Record void) => () => void) | undefined>; + var rgs: Record; } -globalThis.rgs = {}; -globalThis.setters = {}; -globalThis.subscribers = {}; +const globalThisForBetterMinification = globalThis; +globalThisForBetterMinification.rgs = {}; +const globalRGS = globalThisForBetterMinification.rgs; + +/** Initialize the named store when invoked for the first time. */ +function init(key: string, value?: T) { + const listeners: Listener[] = []; + /** setter function to set the state. */ + const setter: SetStateAction = val => { + const rgs = globalRGS[key] as RGS; + rgs[VALUE] = val instanceof Function ? val(rgs[VALUE] as T) : val; + (rgs[LISTENERS] as Listener[]).forEach(listener => listener()); + }; + /** subscriber function to subscribe to the store. */ + const subscriber: Subscriber = listener => { + const rgs = globalRGS[key] as RGS; + const listeners = rgs[LISTENERS] as Listener[]; + listeners.push(listener); + return () => { + rgs[LISTENERS] = listeners.filter(l => l !== listener); + }; + }; + globalRGS[key] = [value, listeners, setter as SetStateAction, subscriber]; +} /** * Use this hook similar to `useState` hook. @@ -34,34 +53,22 @@ globalThis.subscribers = {}; * const [state, setState] = useRGS("counter", 1); * ``` */ -export default function useRGS(key: string, value?: T): [T, (val: SetterArgType) => void] { - if (!globalThis.subscribers[key]) { - globalThis.subscribers[key] = (listener: () => void) => { - if (!globalThis.rgs[key]) { - /** opportunity to add initializer */ - globalThis.rgs[key] = { listeners: [], value }; - } - const rgs = globalThis.rgs[key] as React18GlobalStore; - rgs.listeners.push(listener); - return () => { - rgs.listeners = rgs.listeners.filter(l => l !== listener); - }; - }; - } - const subscribe = globalThis.subscribers[key] as (listener: () => void) => () => void; +export default function useRGS( + key: string, + value?: T, + serverValue?: T, +): [T, (val: SetterArgType) => void] { + if (!globalRGS[key]) init(key, value); - if (!globalThis.setters[key]) { - globalThis.setters[key] = val => { - const rgs = globalThis.rgs[key] as React18GlobalStore; - rgs.value = val instanceof Function ? val(rgs.value as T) : val; - /** opportunity to add custom listener */ - for (const listener of rgs.listeners) listener(); - }; - } - const setRGState = globalThis.setters[key] as SetStateAction; + const rgs = globalRGS[key] as RGS; - const getSnapshot = () => (globalThis.rgs[key]?.value ?? value) as T; + /** Function to set the state. */ + const setRGState = rgs[SETTER] as SetStateAction; + /** Function to get snapshot of the state. */ + const getSnap = () => (rgs[VALUE] ?? value) as T; + /** Function to get server snapshot. Returns server value is provided else the default value. */ + const getServerSnap = () => (serverValue ?? rgs[VALUE] ?? value) as T; - const val = useSyncExternalStore(subscribe, getSnapshot, getSnapshot); + const val = useSyncExternalStore(rgs[SUBSCRIBER] as Subscriber, getSnap, getServerSnap); return [val, setRGState]; } diff --git a/packages/shared-ui/src/root-layout.tsx b/packages/shared-ui/src/root-layout.tsx index 650b82d8..171001ae 100644 --- a/packages/shared-ui/src/root-layout.tsx +++ b/packages/shared-ui/src/root-layout.tsx @@ -1,7 +1,7 @@ import "./globals.css"; import "nthul-lite/styles.css"; import { ThemeSwitcher } from "nthul-lite/client/theme-switcher"; -import { ForkMe } from "@mayank1513/fork-me/server"; // todo: import directory not supported in remix +import { ForkMe } from "@mayank1513/fork-me/server"; import type { HTMLProps } from "react"; import styles from "./root-layout.module.css"; import { Cards } from "./cards";