@pixotope/react-context-store 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
 
2
- > @pixotope/react-context-store@0.2.0 build /home/circleci/project/packages/react-context-store
3
- > tsup src/index.ts --format cjs,esm --dts --sourcemap --external react --external use-sync-external-store
2
+ > @pixotope/react-context-store@0.4.0 build /home/circleci/project/packages/react-context-store
3
+ > tsup src/index.ts --format cjs,esm --dts --sourcemap --external react
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
@@ -8,13 +8,13 @@
8
8
  CLI Target: es6
9
9
  CJS Build start
10
10
  ESM Build start
11
- ESM dist/index.js 7.92 KB
12
- ESM dist/index.js.map 16.10 KB
13
- ESM ⚡️ Build success in 24ms
14
- CJS dist/index.cjs 8.55 KB
15
- CJS dist/index.cjs.map 16.23 KB
16
- CJS ⚡️ Build success in 24ms
11
+ CJS dist/index.cjs 8.57 KB
12
+ CJS dist/index.cjs.map 16.37 KB
13
+ CJS ⚡️ Build success in 68ms
14
+ ESM dist/index.js 7.96 KB
15
+ ESM dist/index.js.map 16.25 KB
16
+ ESM ⚡️ Build success in 69ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 1731ms
19
- DTS dist/index.d.cts 2.31 KB
20
- DTS dist/index.d.ts 2.31 KB
18
+ DTS ⚡️ Build success in 3560ms
19
+ DTS dist/index.d.cts 2.34 KB
20
+ DTS dist/index.d.ts 2.34 KB
@@ -1,4 +1,4 @@
1
1
 
2
- > @pixotope/react-context-store@0.2.0 check-types /home/circleci/project/packages/react-context-store
2
+ > @pixotope/react-context-store@0.4.0 check-types /home/circleci/project/packages/react-context-store
3
3
  > tsc --noEmit
4
4
 
@@ -1,14 +1,14 @@
1
1
 
2
- > @pixotope/react-context-store@0.2.0 test /home/circleci/project/packages/react-context-store
2
+ > @pixotope/react-context-store@0.4.0 test /home/circleci/project/packages/react-context-store
3
3
  > vitest
4
4
 
5
5
 
6
6
   RUN  v3.2.4 /home/circleci/project/packages/react-context-store
7
7
 
8
- ✓ src/store.test.tsx (12 tests) 102ms
8
+ ✓ src/store.test.tsx (14 tests) 163ms
9
9
 
10
10
   Test Files  1 passed (1)
11
-  Tests  12 passed (12)
12
-  Start at  17:28:09
13
-  Duration  3.39s (transform 157ms, setup 117ms, collect 365ms, tests 102ms, environment 887ms, prepare 711ms)
11
+  Tests  14 passed (14)
12
+  Start at  17:40:29
13
+  Duration  7.05s (transform 626ms, setup 310ms, collect 804ms, tests 163ms, environment 2.10s, prepare 1.52s)
14
14
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @pixotope/react-context-store
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a0b9271:  - ✨ enhance actions and include related tests
8
+
9
+ ## 0.3.0
10
+
11
+ ### Minor Changes
12
+
13
+ - eec8876: - remove `use-sync-external-store` and force peer react version to min 18
14
+
3
15
  ## 0.2.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -105,7 +105,6 @@ module.exports = __toCommonJS(index_exports);
105
105
  // src/store.tsx
106
106
  var import_react = __toESM(require("react"), 1);
107
107
  var import_comparison = __toESM(require_comparison(), 1);
108
- var import_shim = require("use-sync-external-store/shim");
109
108
  var LIB_NAME = "@pixotope/react-context-store";
110
109
  function isFunction(value) {
111
110
  return typeof value === "function";
@@ -184,7 +183,7 @@ function createContextStore(initialState, actions = {}, options = {}) {
184
183
  `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`
185
184
  );
186
185
  }
187
- const state = (0, import_shim.useSyncExternalStore)(
186
+ const state = (0, import_react.useSyncExternalStore)(
188
187
  store.subscribe,
189
188
  () => {
190
189
  const selectedState = selector(store.get());
@@ -236,11 +235,13 @@ function createContextStore(initialState, actions = {}, options = {}) {
236
235
  return actionProxy;
237
236
  }
238
237
  const useSetStore = () => useStore(() => false).set;
238
+ const useStoreSelector = () => useStore(() => false).selector;
239
239
  return {
240
240
  Provider,
241
241
  useStore,
242
242
  useSetStore,
243
243
  useActions,
244
+ useStoreSelector,
244
245
  subscribe: observable.subscribe,
245
246
  unsubscribe: observable.unsubscribe
246
247
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../utils/dist/comparison/index.js","../src/index.ts","../src/store.tsx"],"sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/comparison/index.ts\nvar comparison_exports = {};\n__export(comparison_exports, {\n shallowEqual: () => shallowEqual\n});\nmodule.exports = __toCommonJS(comparison_exports);\n\n// src/comparison/shallowEqual.ts\nfunction shallowEqual(objA, objB) {\n if (Object.is(objA, objB)) {\n return true;\n }\n if (typeof objA !== \"object\" || objA === null || typeof objB !== \"object\" || objB === null) {\n return false;\n }\n if (objA instanceof Map && objB instanceof Map) {\n if (objA.size !== objB.size) return false;\n for (const [key, value] of objA) {\n if (!Object.is(value, objB.get(key))) {\n return false;\n }\n }\n return true;\n }\n if (objA instanceof Set && objB instanceof Set) {\n if (objA.size !== objB.size) return false;\n for (const value of objA) {\n if (!objB.has(value)) {\n return false;\n }\n }\n return true;\n }\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) {\n return false;\n }\n for (let i = 0; i < keysA.length; i++) {\n if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {\n return false;\n }\n }\n return true;\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n shallowEqual\n});\n","export * from \"./store\";\n","import React, {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n} from \"react\";\nimport { shallowEqual } from \"@pixotope/utils/comparison\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim\";\n\nconst LIB_NAME = \"@pixotope/react-context-store\";\n\nexport type SelectorOptions<Selected> = (\n first: Selected,\n second: Selected\n) => boolean;\n\nexport type ContextOptions = {\n /**\n * If true, the store state will be preserved across mounts and un-mounts\n * of the Provider.\n * @default false\n */\n global?: boolean;\n /**\n * A function that will be used to compare the selected state.\n * @default shallowEqual\n */\n compare?: SelectorOptions<unknown>;\n};\n\nexport type SetterArgs<Store> = Store | ((prev: Store) => Store);\ntype ExtractActionKeys<T> = {\n [K in keyof T]: T[K] extends (\n stateProps: never,\n action: infer A\n ) => void | Promise<void>\n ? A extends ActionablePayload<infer Payload>\n ? Payload extends undefined\n ? () => void\n : (payload: Payload) => void\n : () => void\n : never;\n};\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type ActionablePayload<Payload = any> = {\n payload: Payload;\n};\n\ntype StateActionProps<Store> = {\n set: (value: SetterArgs<Store>) => void;\n get: () => Store;\n};\n\ntype Actions<Store> = {\n [key: string]: (\n stateProps: StateActionProps<Store>,\n action: ActionablePayload\n ) => void | Promise<void>;\n};\n\ntype UseStoreReturnType<Store, SelectorOutput> = {\n state: SelectorOutput;\n get: () => SelectorOutput;\n set: (value: SetterArgs<Store>) => void;\n selector: () => Store;\n};\n\ntype ContextReturnType<Store, A extends Actions<Store>> = {\n Provider: React.FC<React.PropsWithChildren>;\n useStore: <SelectorOutput = Store>(\n selector?: (store: Store) => SelectorOutput,\n options?: SelectorOptions<SelectorOutput>\n ) => UseStoreReturnType<Store, SelectorOutput>;\n useActions: () => Prettify<ExtractActionKeys<A>>;\n useSetStore: () => (value: SetterArgs<Store>) => void;\n subscribe: <SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput,\n callback: (state: SelectorOutput) => void,\n options?: SelectorOptions<SelectorOutput>\n ) => () => void;\n unsubscribe: (callback: () => void) => void;\n};\n\nfunction isFunction(value: any): value is (prev: any) => any {\n return typeof value === \"function\";\n}\n\nexport function createContextStore<Store, A extends Actions<Store> = {}>(\n ...args: Extract<A, { payload: A }> extends { payload: infer Payload }\n ? [initialState: Store, actions?: A, options?: ContextOptions]\n : [initialState: Store, options?: ContextOptions]\n): ContextReturnType<Store, A>;\nexport function createContextStore<Store, A extends Actions<Store> = {}>(\n initialState: Store,\n actions: A = {} as A,\n options: ContextOptions = {}\n) {\n let globalStore: Store | undefined = options.global\n ? initialState\n : undefined;\n\n function useStoreData({\n defaultSubscriber = [],\n }: {\n defaultSubscriber?: ((state: Store) => void)[];\n } = {}): {\n get: () => Store;\n set: (value: SetterArgs<Store>) => void;\n subscribe: (callback: (state: Store) => void) => () => void;\n } {\n const store = useRef<Store>(globalStore ?? initialState);\n\n const get = useCallback(() => store.current, []);\n\n const subscribers = useRef(\n new Set<(state: Store) => void>(defaultSubscriber)\n );\n\n const set = useCallback((value: SetterArgs<Store>) => {\n store.current = isFunction(value) ? value(store.current) : value;\n\n if (options.global) {\n globalStore = store.current;\n }\n\n subscribers.current.forEach((callback) => callback(store.current));\n }, []);\n\n const subscribe = useCallback((callback: (state: Store) => void) => {\n subscribers.current.add(callback);\n\n return () => subscribers.current.delete(callback);\n }, []);\n\n return {\n get,\n set,\n subscribe,\n };\n }\n\n type UseStoreDataReturnType = ReturnType<typeof useStoreData>;\n\n const StoreContext = createContext<UseStoreDataReturnType | null>(null);\n\n /**\n * This is the provider that will be used to wrap the react component tree\n * to provide the store to all the components in the tree.\n */\n function Provider({ children }: React.PropsWithChildren) {\n return (\n <StoreContext.Provider\n value={useStoreData({ defaultSubscriber: [observable.broadcast] })}\n >\n {children}\n </StoreContext.Provider>\n );\n }\n\n /**\n * This holds all the observables that are subscribed to the store\n * but not being actively used by any component. Helps to broadcast\n * store updates to all the observables that are interested in reacting\n * to state outside the react component tree.\n */\n const observers = new Set<(state: Store) => void>();\n\n function subscribeExternal<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n callback: (state: SelectorOutput) => void,\n compare: SelectorOptions<SelectorOutput> = shallowEqual\n ): () => void {\n let lastSelectedState: SelectorOutput | undefined;\n\n const shouldSendUpdates = (newSelectedState: SelectorOutput) => {\n if (\n lastSelectedState === undefined ||\n !compare(lastSelectedState, newSelectedState)\n ) {\n lastSelectedState = newSelectedState;\n\n return true;\n }\n\n return false;\n };\n\n const selectedCb = (store: Store) => {\n const selected = selector(store);\n const shouldSend = shouldSendUpdates(selected);\n\n if (shouldSend) {\n callback(selector(store));\n }\n };\n observers.add(selectedCb);\n\n return () => observers.delete(selectedCb);\n }\n\n /**\n * A simple observable that proxy store updates to all the subscribers\n * that are interested in reacting to state outside the react component tree.\n */\n const observable = {\n subscribe: subscribeExternal,\n unsubscribe: (callback: (state: Store) => void) => {\n observers.delete(callback);\n },\n broadcast: (state: Store) => {\n observers.forEach((callback) => callback(state));\n },\n } as const;\n\n /**\n * This is the hook that will be used to access the store from any component\n * in the react component tree.\n * @param selector A function that will be used to select the part of the store\n * that is needed by the component.\n * @param options Options to customize the behavior of the hook.\n * @returns An object with the selected state, a function to update the store\n * and a function to get the entire store.\n * @example\n * ```tsx\n * const { get, set } = useStore(store => store.user);\n * const { get, set } = useStore(store => store.user, { deepEqual: false });\n * const { get, set } = useStore(store => store.user, {\n * compare: (first, second) => first.id === second.id\n * });\n * ```\n */\n function useStore<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n compare: SelectorOptions<SelectorOutput> = options.compare ?? shallowEqual\n ): UseStoreReturnType<Store, SelectorOutput> {\n const store = useContext(StoreContext);\n const lastSelectedState = useRef<SelectorOutput | undefined>(undefined);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const state = useSyncExternalStore(\n store.subscribe,\n () => {\n const selectedState = selector(store.get());\n\n if (\n lastSelectedState.current === undefined ||\n !compare(lastSelectedState.current, selectedState)\n ) {\n lastSelectedState.current = selectedState;\n }\n\n return lastSelectedState.current;\n },\n () => selector(initialState)\n );\n\n return {\n state,\n get: () => state,\n set: store.set,\n selector: () => store.get(),\n };\n }\n\n function useActions() {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const actionProxy = useMemo(\n () =>\n new Proxy(actions, {\n get: (target, prop) => {\n const action = target[prop as string];\n\n if (action) {\n return (args: Parameters<typeof action>[\"1\"]) => {\n action(\n {\n set: store.set,\n get: store.get,\n },\n {\n payload: args,\n }\n );\n };\n }\n },\n set: () => {\n throw new Error(`[${LIB_NAME}] Actions cannot be updated`);\n },\n }),\n []\n );\n\n return actionProxy as unknown as ExtractActionKeys<typeof actions>;\n }\n\n const useSetStore = () => useStore(() => false).set;\n\n return {\n Provider,\n useStore,\n useSetStore,\n useActions,\n subscribe: observable.subscribe,\n unsubscribe: observable.unsubscribe,\n };\n}\n"],"mappings":"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","names":["exports","module","__defProp","__getOwnPropDesc","__getOwnPropNames","__hasOwnProp","__export","__copyProps","__toCommonJS","shallowEqual","React"]}
1
+ {"version":3,"sources":["../../utils/dist/comparison/index.js","../src/index.ts","../src/store.tsx"],"sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/comparison/index.ts\nvar comparison_exports = {};\n__export(comparison_exports, {\n shallowEqual: () => shallowEqual\n});\nmodule.exports = __toCommonJS(comparison_exports);\n\n// src/comparison/shallowEqual.ts\nfunction shallowEqual(objA, objB) {\n if (Object.is(objA, objB)) {\n return true;\n }\n if (typeof objA !== \"object\" || objA === null || typeof objB !== \"object\" || objB === null) {\n return false;\n }\n if (objA instanceof Map && objB instanceof Map) {\n if (objA.size !== objB.size) return false;\n for (const [key, value] of objA) {\n if (!Object.is(value, objB.get(key))) {\n return false;\n }\n }\n return true;\n }\n if (objA instanceof Set && objB instanceof Set) {\n if (objA.size !== objB.size) return false;\n for (const value of objA) {\n if (!objB.has(value)) {\n return false;\n }\n }\n return true;\n }\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) {\n return false;\n }\n for (let i = 0; i < keysA.length; i++) {\n if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {\n return false;\n }\n }\n return true;\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n shallowEqual\n});\n","export * from \"./store\";\n","import React, {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { shallowEqual } from \"@pixotope/utils/comparison\";\n\nconst LIB_NAME = \"@pixotope/react-context-store\";\n\nexport type SelectorOptions<Selected> = (\n first: Selected,\n second: Selected\n) => boolean;\n\nexport type ContextOptions = {\n /**\n * If true, the store state will be preserved across mounts and un-mounts\n * of the Provider.\n * @default false\n */\n global?: boolean;\n /**\n * A function that will be used to compare the selected state.\n * @default shallowEqual\n */\n compare?: SelectorOptions<unknown>;\n};\n\nexport type SetterArgs<Store> = Store | ((prev: Store) => Store);\ntype ExtractActionKeys<T> = {\n [K in keyof T]: T[K] extends (\n stateProps: never,\n action: infer A\n ) => void | Promise<void>\n ? A extends ActionablePayload<infer Payload>\n ? Payload extends undefined\n ? () => void\n : (payload: Payload) => void\n : () => void\n : never;\n};\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type ActionablePayload<Payload = any> = {\n payload: Payload;\n};\n\ntype StateActionProps<Store> = {\n set: (value: SetterArgs<Store>) => void;\n get: () => Store;\n};\n\ntype Actions<Store> = {\n [key: string]: (\n stateProps: StateActionProps<Store>,\n action: ActionablePayload\n ) => void | Promise<void>;\n};\n\ntype UseStoreReturnType<Store, SelectorOutput> = {\n state: SelectorOutput;\n get: () => SelectorOutput;\n set: (value: SetterArgs<Store>) => void;\n selector: () => Store;\n};\n\ntype ContextReturnType<Store, A extends Actions<Store>> = {\n Provider: React.FC<React.PropsWithChildren>;\n useStore: <SelectorOutput = Store>(\n selector?: (store: Store) => SelectorOutput,\n options?: SelectorOptions<SelectorOutput>\n ) => UseStoreReturnType<Store, SelectorOutput>;\n useActions: () => Prettify<ExtractActionKeys<A>>;\n useSetStore: () => (value: SetterArgs<Store>) => void;\n useStoreSelector: () => () => Store;\n subscribe: <SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput,\n callback: (state: SelectorOutput) => void,\n options?: SelectorOptions<SelectorOutput>\n ) => () => void;\n unsubscribe: (callback: () => void) => void;\n};\n\nfunction isFunction(value: any): value is (prev: any) => any {\n return typeof value === \"function\";\n}\n\nexport function createContextStore<Store, A extends Actions<Store>>(\n ...args: Extract<A, { payload: A }> extends { payload: infer Payload }\n ? [initialState: Store, actions?: A, options?: ContextOptions]\n : [initialState: Store, options?: ContextOptions]\n): ContextReturnType<Store, A>;\nexport function createContextStore<Store, A extends Actions<Store>>(\n initialState: Store,\n actions: A = {} as A,\n options: ContextOptions = {}\n): ContextReturnType<Store, A> {\n let globalStore: Store | undefined = options.global\n ? initialState\n : undefined;\n\n function useStoreData({\n defaultSubscriber = [],\n }: {\n defaultSubscriber?: ((state: Store) => void)[];\n } = {}): {\n get: () => Store;\n set: (value: SetterArgs<Store>) => void;\n subscribe: (callback: (state: Store) => void) => () => void;\n } {\n const store = useRef<Store>(globalStore ?? initialState);\n\n const get = useCallback(() => store.current, []);\n\n const subscribers = useRef(\n new Set<(state: Store) => void>(defaultSubscriber)\n );\n\n const set = useCallback((value: SetterArgs<Store>) => {\n store.current = isFunction(value) ? value(store.current) : value;\n\n if (options.global) {\n globalStore = store.current;\n }\n\n subscribers.current.forEach((callback) => callback(store.current));\n }, []);\n\n const subscribe = useCallback((callback: (state: Store) => void) => {\n subscribers.current.add(callback);\n\n return () => subscribers.current.delete(callback);\n }, []);\n\n return {\n get,\n set,\n subscribe,\n };\n }\n\n type UseStoreDataReturnType = ReturnType<typeof useStoreData>;\n\n const StoreContext = createContext<UseStoreDataReturnType | null>(null);\n\n /**\n * This is the provider that will be used to wrap the react component tree\n * to provide the store to all the components in the tree.\n */\n function Provider({ children }: React.PropsWithChildren) {\n return (\n <StoreContext.Provider\n value={useStoreData({ defaultSubscriber: [observable.broadcast] })}\n >\n {children}\n </StoreContext.Provider>\n );\n }\n\n /**\n * This holds all the observables that are subscribed to the store\n * but not being actively used by any component. Helps to broadcast\n * store updates to all the observables that are interested in reacting\n * to state outside the react component tree.\n */\n const observers = new Set<(state: Store) => void>();\n\n function subscribeExternal<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n callback: (state: SelectorOutput) => void,\n compare: SelectorOptions<SelectorOutput> = shallowEqual\n ): () => void {\n let lastSelectedState: SelectorOutput | undefined;\n\n const shouldSendUpdates = (newSelectedState: SelectorOutput) => {\n if (\n lastSelectedState === undefined ||\n !compare(lastSelectedState, newSelectedState)\n ) {\n lastSelectedState = newSelectedState;\n\n return true;\n }\n\n return false;\n };\n\n const selectedCb = (store: Store) => {\n const selected = selector(store);\n const shouldSend = shouldSendUpdates(selected);\n\n if (shouldSend) {\n callback(selector(store));\n }\n };\n observers.add(selectedCb);\n\n return () => observers.delete(selectedCb);\n }\n\n /**\n * A simple observable that proxy store updates to all the subscribers\n * that are interested in reacting to state outside the react component tree.\n */\n const observable = {\n subscribe: subscribeExternal,\n unsubscribe: (callback: (state: Store) => void) => {\n observers.delete(callback);\n },\n broadcast: (state: Store) => {\n observers.forEach((callback) => callback(state));\n },\n } as const;\n\n /**\n * This is the hook that will be used to access the store from any component\n * in the react component tree.\n * @param selector A function that will be used to select the part of the store\n * that is needed by the component.\n * @param options Options to customize the behavior of the hook.\n * @returns An object with the selected state, a function to update the store\n * and a function to get the entire store.\n * @example\n * ```tsx\n * const { get, set } = useStore(store => store.user);\n * const { get, set } = useStore(store => store.user, { deepEqual: false });\n * const { get, set } = useStore(store => store.user, {\n * compare: (first, second) => first.id === second.id\n * });\n * ```\n */\n function useStore<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n compare: SelectorOptions<SelectorOutput> = options.compare ?? shallowEqual\n ): UseStoreReturnType<Store, SelectorOutput> {\n const store = useContext(StoreContext);\n const lastSelectedState = useRef<SelectorOutput | undefined>(undefined);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const state = useSyncExternalStore(\n store.subscribe,\n () => {\n const selectedState = selector(store.get());\n\n if (\n lastSelectedState.current === undefined ||\n !compare(lastSelectedState.current, selectedState)\n ) {\n lastSelectedState.current = selectedState;\n }\n\n return lastSelectedState.current;\n },\n () => selector(initialState)\n );\n\n return {\n state,\n get: () => state,\n set: store.set,\n selector: () => store.get(),\n };\n }\n\n function useActions() {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const actionProxy = useMemo(\n () =>\n new Proxy(actions, {\n get: (target, prop) => {\n const action = target[prop as string];\n\n if (action) {\n return (args: Parameters<typeof action>[\"1\"]) => {\n action(\n {\n set: store.set,\n get: store.get,\n },\n {\n payload: args,\n }\n );\n };\n }\n },\n set: () => {\n throw new Error(`[${LIB_NAME}] Actions cannot be updated`);\n },\n }),\n []\n );\n\n return actionProxy as unknown as ExtractActionKeys<typeof actions>;\n }\n\n const useSetStore = () => useStore(() => false).set;\n const useStoreSelector = () => useStore(() => false).selector;\n\n return {\n Provider,\n useStore,\n useSetStore,\n useActions,\n useStoreSelector,\n subscribe: observable.subscribe,\n unsubscribe: observable.unsubscribe,\n };\n}\n"],"mappings":"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","names":["exports","module","__defProp","__getOwnPropDesc","__getOwnPropNames","__hasOwnProp","__export","__copyProps","__toCommonJS","shallowEqual","React"]}
package/dist/index.d.cts CHANGED
@@ -42,10 +42,11 @@ type ContextReturnType<Store, A extends Actions<Store>> = {
42
42
  useStore: <SelectorOutput = Store>(selector?: (store: Store) => SelectorOutput, options?: SelectorOptions<SelectorOutput>) => UseStoreReturnType<Store, SelectorOutput>;
43
43
  useActions: () => Prettify<ExtractActionKeys<A>>;
44
44
  useSetStore: () => (value: SetterArgs<Store>) => void;
45
+ useStoreSelector: () => () => Store;
45
46
  subscribe: <SelectorOutput = Store>(selector: (store: Store) => SelectorOutput, callback: (state: SelectorOutput) => void, options?: SelectorOptions<SelectorOutput>) => () => void;
46
47
  unsubscribe: (callback: () => void) => void;
47
48
  };
48
- declare function createContextStore<Store, A extends Actions<Store> = {}>(...args: Extract<A, {
49
+ declare function createContextStore<Store, A extends Actions<Store>>(...args: Extract<A, {
49
50
  payload: A;
50
51
  }> extends {
51
52
  payload: infer Payload;
package/dist/index.d.ts CHANGED
@@ -42,10 +42,11 @@ type ContextReturnType<Store, A extends Actions<Store>> = {
42
42
  useStore: <SelectorOutput = Store>(selector?: (store: Store) => SelectorOutput, options?: SelectorOptions<SelectorOutput>) => UseStoreReturnType<Store, SelectorOutput>;
43
43
  useActions: () => Prettify<ExtractActionKeys<A>>;
44
44
  useSetStore: () => (value: SetterArgs<Store>) => void;
45
+ useStoreSelector: () => () => Store;
45
46
  subscribe: <SelectorOutput = Store>(selector: (store: Store) => SelectorOutput, callback: (state: SelectorOutput) => void, options?: SelectorOptions<SelectorOutput>) => () => void;
46
47
  unsubscribe: (callback: () => void) => void;
47
48
  };
48
- declare function createContextStore<Store, A extends Actions<Store> = {}>(...args: Extract<A, {
49
+ declare function createContextStore<Store, A extends Actions<Store>>(...args: Extract<A, {
49
50
  payload: A;
50
51
  }> extends {
51
52
  payload: infer Payload;
package/dist/index.js CHANGED
@@ -96,9 +96,9 @@ import React, {
96
96
  useCallback,
97
97
  useContext,
98
98
  useMemo,
99
- useRef
99
+ useRef,
100
+ useSyncExternalStore
100
101
  } from "react";
101
- import { useSyncExternalStore } from "use-sync-external-store/shim";
102
102
  var LIB_NAME = "@pixotope/react-context-store";
103
103
  function isFunction(value) {
104
104
  return typeof value === "function";
@@ -229,11 +229,13 @@ function createContextStore(initialState, actions = {}, options = {}) {
229
229
  return actionProxy;
230
230
  }
231
231
  const useSetStore = () => useStore(() => false).set;
232
+ const useStoreSelector = () => useStore(() => false).selector;
232
233
  return {
233
234
  Provider,
234
235
  useStore,
235
236
  useSetStore,
236
237
  useActions,
238
+ useStoreSelector,
237
239
  subscribe: observable.subscribe,
238
240
  unsubscribe: observable.unsubscribe
239
241
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../utils/dist/comparison/index.js","../src/store.tsx"],"sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/comparison/index.ts\nvar comparison_exports = {};\n__export(comparison_exports, {\n shallowEqual: () => shallowEqual\n});\nmodule.exports = __toCommonJS(comparison_exports);\n\n// src/comparison/shallowEqual.ts\nfunction shallowEqual(objA, objB) {\n if (Object.is(objA, objB)) {\n return true;\n }\n if (typeof objA !== \"object\" || objA === null || typeof objB !== \"object\" || objB === null) {\n return false;\n }\n if (objA instanceof Map && objB instanceof Map) {\n if (objA.size !== objB.size) return false;\n for (const [key, value] of objA) {\n if (!Object.is(value, objB.get(key))) {\n return false;\n }\n }\n return true;\n }\n if (objA instanceof Set && objB instanceof Set) {\n if (objA.size !== objB.size) return false;\n for (const value of objA) {\n if (!objB.has(value)) {\n return false;\n }\n }\n return true;\n }\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) {\n return false;\n }\n for (let i = 0; i < keysA.length; i++) {\n if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {\n return false;\n }\n }\n return true;\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n shallowEqual\n});\n","import React, {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n} from \"react\";\nimport { shallowEqual } from \"@pixotope/utils/comparison\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim\";\n\nconst LIB_NAME = \"@pixotope/react-context-store\";\n\nexport type SelectorOptions<Selected> = (\n first: Selected,\n second: Selected\n) => boolean;\n\nexport type ContextOptions = {\n /**\n * If true, the store state will be preserved across mounts and un-mounts\n * of the Provider.\n * @default false\n */\n global?: boolean;\n /**\n * A function that will be used to compare the selected state.\n * @default shallowEqual\n */\n compare?: SelectorOptions<unknown>;\n};\n\nexport type SetterArgs<Store> = Store | ((prev: Store) => Store);\ntype ExtractActionKeys<T> = {\n [K in keyof T]: T[K] extends (\n stateProps: never,\n action: infer A\n ) => void | Promise<void>\n ? A extends ActionablePayload<infer Payload>\n ? Payload extends undefined\n ? () => void\n : (payload: Payload) => void\n : () => void\n : never;\n};\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type ActionablePayload<Payload = any> = {\n payload: Payload;\n};\n\ntype StateActionProps<Store> = {\n set: (value: SetterArgs<Store>) => void;\n get: () => Store;\n};\n\ntype Actions<Store> = {\n [key: string]: (\n stateProps: StateActionProps<Store>,\n action: ActionablePayload\n ) => void | Promise<void>;\n};\n\ntype UseStoreReturnType<Store, SelectorOutput> = {\n state: SelectorOutput;\n get: () => SelectorOutput;\n set: (value: SetterArgs<Store>) => void;\n selector: () => Store;\n};\n\ntype ContextReturnType<Store, A extends Actions<Store>> = {\n Provider: React.FC<React.PropsWithChildren>;\n useStore: <SelectorOutput = Store>(\n selector?: (store: Store) => SelectorOutput,\n options?: SelectorOptions<SelectorOutput>\n ) => UseStoreReturnType<Store, SelectorOutput>;\n useActions: () => Prettify<ExtractActionKeys<A>>;\n useSetStore: () => (value: SetterArgs<Store>) => void;\n subscribe: <SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput,\n callback: (state: SelectorOutput) => void,\n options?: SelectorOptions<SelectorOutput>\n ) => () => void;\n unsubscribe: (callback: () => void) => void;\n};\n\nfunction isFunction(value: any): value is (prev: any) => any {\n return typeof value === \"function\";\n}\n\nexport function createContextStore<Store, A extends Actions<Store> = {}>(\n ...args: Extract<A, { payload: A }> extends { payload: infer Payload }\n ? [initialState: Store, actions?: A, options?: ContextOptions]\n : [initialState: Store, options?: ContextOptions]\n): ContextReturnType<Store, A>;\nexport function createContextStore<Store, A extends Actions<Store> = {}>(\n initialState: Store,\n actions: A = {} as A,\n options: ContextOptions = {}\n) {\n let globalStore: Store | undefined = options.global\n ? initialState\n : undefined;\n\n function useStoreData({\n defaultSubscriber = [],\n }: {\n defaultSubscriber?: ((state: Store) => void)[];\n } = {}): {\n get: () => Store;\n set: (value: SetterArgs<Store>) => void;\n subscribe: (callback: (state: Store) => void) => () => void;\n } {\n const store = useRef<Store>(globalStore ?? initialState);\n\n const get = useCallback(() => store.current, []);\n\n const subscribers = useRef(\n new Set<(state: Store) => void>(defaultSubscriber)\n );\n\n const set = useCallback((value: SetterArgs<Store>) => {\n store.current = isFunction(value) ? value(store.current) : value;\n\n if (options.global) {\n globalStore = store.current;\n }\n\n subscribers.current.forEach((callback) => callback(store.current));\n }, []);\n\n const subscribe = useCallback((callback: (state: Store) => void) => {\n subscribers.current.add(callback);\n\n return () => subscribers.current.delete(callback);\n }, []);\n\n return {\n get,\n set,\n subscribe,\n };\n }\n\n type UseStoreDataReturnType = ReturnType<typeof useStoreData>;\n\n const StoreContext = createContext<UseStoreDataReturnType | null>(null);\n\n /**\n * This is the provider that will be used to wrap the react component tree\n * to provide the store to all the components in the tree.\n */\n function Provider({ children }: React.PropsWithChildren) {\n return (\n <StoreContext.Provider\n value={useStoreData({ defaultSubscriber: [observable.broadcast] })}\n >\n {children}\n </StoreContext.Provider>\n );\n }\n\n /**\n * This holds all the observables that are subscribed to the store\n * but not being actively used by any component. Helps to broadcast\n * store updates to all the observables that are interested in reacting\n * to state outside the react component tree.\n */\n const observers = new Set<(state: Store) => void>();\n\n function subscribeExternal<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n callback: (state: SelectorOutput) => void,\n compare: SelectorOptions<SelectorOutput> = shallowEqual\n ): () => void {\n let lastSelectedState: SelectorOutput | undefined;\n\n const shouldSendUpdates = (newSelectedState: SelectorOutput) => {\n if (\n lastSelectedState === undefined ||\n !compare(lastSelectedState, newSelectedState)\n ) {\n lastSelectedState = newSelectedState;\n\n return true;\n }\n\n return false;\n };\n\n const selectedCb = (store: Store) => {\n const selected = selector(store);\n const shouldSend = shouldSendUpdates(selected);\n\n if (shouldSend) {\n callback(selector(store));\n }\n };\n observers.add(selectedCb);\n\n return () => observers.delete(selectedCb);\n }\n\n /**\n * A simple observable that proxy store updates to all the subscribers\n * that are interested in reacting to state outside the react component tree.\n */\n const observable = {\n subscribe: subscribeExternal,\n unsubscribe: (callback: (state: Store) => void) => {\n observers.delete(callback);\n },\n broadcast: (state: Store) => {\n observers.forEach((callback) => callback(state));\n },\n } as const;\n\n /**\n * This is the hook that will be used to access the store from any component\n * in the react component tree.\n * @param selector A function that will be used to select the part of the store\n * that is needed by the component.\n * @param options Options to customize the behavior of the hook.\n * @returns An object with the selected state, a function to update the store\n * and a function to get the entire store.\n * @example\n * ```tsx\n * const { get, set } = useStore(store => store.user);\n * const { get, set } = useStore(store => store.user, { deepEqual: false });\n * const { get, set } = useStore(store => store.user, {\n * compare: (first, second) => first.id === second.id\n * });\n * ```\n */\n function useStore<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n compare: SelectorOptions<SelectorOutput> = options.compare ?? shallowEqual\n ): UseStoreReturnType<Store, SelectorOutput> {\n const store = useContext(StoreContext);\n const lastSelectedState = useRef<SelectorOutput | undefined>(undefined);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const state = useSyncExternalStore(\n store.subscribe,\n () => {\n const selectedState = selector(store.get());\n\n if (\n lastSelectedState.current === undefined ||\n !compare(lastSelectedState.current, selectedState)\n ) {\n lastSelectedState.current = selectedState;\n }\n\n return lastSelectedState.current;\n },\n () => selector(initialState)\n );\n\n return {\n state,\n get: () => state,\n set: store.set,\n selector: () => store.get(),\n };\n }\n\n function useActions() {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const actionProxy = useMemo(\n () =>\n new Proxy(actions, {\n get: (target, prop) => {\n const action = target[prop as string];\n\n if (action) {\n return (args: Parameters<typeof action>[\"1\"]) => {\n action(\n {\n set: store.set,\n get: store.get,\n },\n {\n payload: args,\n }\n );\n };\n }\n },\n set: () => {\n throw new Error(`[${LIB_NAME}] Actions cannot be updated`);\n },\n }),\n []\n );\n\n return actionProxy as unknown as ExtractActionKeys<typeof actions>;\n }\n\n const useSetStore = () => useStore(() => false).set;\n\n return {\n Provider,\n useStore,\n useSetStore,\n useActions,\n subscribe: observable.subscribe,\n unsubscribe: observable.unsubscribe,\n };\n}\n"],"mappings":"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","names":["__defProp","__getOwnPropDesc","__getOwnPropNames","__hasOwnProp","__copyProps","shallowEqual"]}
1
+ {"version":3,"sources":["../../utils/dist/comparison/index.js","../src/store.tsx"],"sourcesContent":["\"use strict\";\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/comparison/index.ts\nvar comparison_exports = {};\n__export(comparison_exports, {\n shallowEqual: () => shallowEqual\n});\nmodule.exports = __toCommonJS(comparison_exports);\n\n// src/comparison/shallowEqual.ts\nfunction shallowEqual(objA, objB) {\n if (Object.is(objA, objB)) {\n return true;\n }\n if (typeof objA !== \"object\" || objA === null || typeof objB !== \"object\" || objB === null) {\n return false;\n }\n if (objA instanceof Map && objB instanceof Map) {\n if (objA.size !== objB.size) return false;\n for (const [key, value] of objA) {\n if (!Object.is(value, objB.get(key))) {\n return false;\n }\n }\n return true;\n }\n if (objA instanceof Set && objB instanceof Set) {\n if (objA.size !== objB.size) return false;\n for (const value of objA) {\n if (!objB.has(value)) {\n return false;\n }\n }\n return true;\n }\n const keysA = Object.keys(objA);\n if (keysA.length !== Object.keys(objB).length) {\n return false;\n }\n for (let i = 0; i < keysA.length; i++) {\n if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {\n return false;\n }\n }\n return true;\n}\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n shallowEqual\n});\n","import React, {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { shallowEqual } from \"@pixotope/utils/comparison\";\n\nconst LIB_NAME = \"@pixotope/react-context-store\";\n\nexport type SelectorOptions<Selected> = (\n first: Selected,\n second: Selected\n) => boolean;\n\nexport type ContextOptions = {\n /**\n * If true, the store state will be preserved across mounts and un-mounts\n * of the Provider.\n * @default false\n */\n global?: boolean;\n /**\n * A function that will be used to compare the selected state.\n * @default shallowEqual\n */\n compare?: SelectorOptions<unknown>;\n};\n\nexport type SetterArgs<Store> = Store | ((prev: Store) => Store);\ntype ExtractActionKeys<T> = {\n [K in keyof T]: T[K] extends (\n stateProps: never,\n action: infer A\n ) => void | Promise<void>\n ? A extends ActionablePayload<infer Payload>\n ? Payload extends undefined\n ? () => void\n : (payload: Payload) => void\n : () => void\n : never;\n};\n\ntype Prettify<T> = {\n [K in keyof T]: T[K];\n} & {};\n\nexport type ActionablePayload<Payload = any> = {\n payload: Payload;\n};\n\ntype StateActionProps<Store> = {\n set: (value: SetterArgs<Store>) => void;\n get: () => Store;\n};\n\ntype Actions<Store> = {\n [key: string]: (\n stateProps: StateActionProps<Store>,\n action: ActionablePayload\n ) => void | Promise<void>;\n};\n\ntype UseStoreReturnType<Store, SelectorOutput> = {\n state: SelectorOutput;\n get: () => SelectorOutput;\n set: (value: SetterArgs<Store>) => void;\n selector: () => Store;\n};\n\ntype ContextReturnType<Store, A extends Actions<Store>> = {\n Provider: React.FC<React.PropsWithChildren>;\n useStore: <SelectorOutput = Store>(\n selector?: (store: Store) => SelectorOutput,\n options?: SelectorOptions<SelectorOutput>\n ) => UseStoreReturnType<Store, SelectorOutput>;\n useActions: () => Prettify<ExtractActionKeys<A>>;\n useSetStore: () => (value: SetterArgs<Store>) => void;\n useStoreSelector: () => () => Store;\n subscribe: <SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput,\n callback: (state: SelectorOutput) => void,\n options?: SelectorOptions<SelectorOutput>\n ) => () => void;\n unsubscribe: (callback: () => void) => void;\n};\n\nfunction isFunction(value: any): value is (prev: any) => any {\n return typeof value === \"function\";\n}\n\nexport function createContextStore<Store, A extends Actions<Store>>(\n ...args: Extract<A, { payload: A }> extends { payload: infer Payload }\n ? [initialState: Store, actions?: A, options?: ContextOptions]\n : [initialState: Store, options?: ContextOptions]\n): ContextReturnType<Store, A>;\nexport function createContextStore<Store, A extends Actions<Store>>(\n initialState: Store,\n actions: A = {} as A,\n options: ContextOptions = {}\n): ContextReturnType<Store, A> {\n let globalStore: Store | undefined = options.global\n ? initialState\n : undefined;\n\n function useStoreData({\n defaultSubscriber = [],\n }: {\n defaultSubscriber?: ((state: Store) => void)[];\n } = {}): {\n get: () => Store;\n set: (value: SetterArgs<Store>) => void;\n subscribe: (callback: (state: Store) => void) => () => void;\n } {\n const store = useRef<Store>(globalStore ?? initialState);\n\n const get = useCallback(() => store.current, []);\n\n const subscribers = useRef(\n new Set<(state: Store) => void>(defaultSubscriber)\n );\n\n const set = useCallback((value: SetterArgs<Store>) => {\n store.current = isFunction(value) ? value(store.current) : value;\n\n if (options.global) {\n globalStore = store.current;\n }\n\n subscribers.current.forEach((callback) => callback(store.current));\n }, []);\n\n const subscribe = useCallback((callback: (state: Store) => void) => {\n subscribers.current.add(callback);\n\n return () => subscribers.current.delete(callback);\n }, []);\n\n return {\n get,\n set,\n subscribe,\n };\n }\n\n type UseStoreDataReturnType = ReturnType<typeof useStoreData>;\n\n const StoreContext = createContext<UseStoreDataReturnType | null>(null);\n\n /**\n * This is the provider that will be used to wrap the react component tree\n * to provide the store to all the components in the tree.\n */\n function Provider({ children }: React.PropsWithChildren) {\n return (\n <StoreContext.Provider\n value={useStoreData({ defaultSubscriber: [observable.broadcast] })}\n >\n {children}\n </StoreContext.Provider>\n );\n }\n\n /**\n * This holds all the observables that are subscribed to the store\n * but not being actively used by any component. Helps to broadcast\n * store updates to all the observables that are interested in reacting\n * to state outside the react component tree.\n */\n const observers = new Set<(state: Store) => void>();\n\n function subscribeExternal<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n callback: (state: SelectorOutput) => void,\n compare: SelectorOptions<SelectorOutput> = shallowEqual\n ): () => void {\n let lastSelectedState: SelectorOutput | undefined;\n\n const shouldSendUpdates = (newSelectedState: SelectorOutput) => {\n if (\n lastSelectedState === undefined ||\n !compare(lastSelectedState, newSelectedState)\n ) {\n lastSelectedState = newSelectedState;\n\n return true;\n }\n\n return false;\n };\n\n const selectedCb = (store: Store) => {\n const selected = selector(store);\n const shouldSend = shouldSendUpdates(selected);\n\n if (shouldSend) {\n callback(selector(store));\n }\n };\n observers.add(selectedCb);\n\n return () => observers.delete(selectedCb);\n }\n\n /**\n * A simple observable that proxy store updates to all the subscribers\n * that are interested in reacting to state outside the react component tree.\n */\n const observable = {\n subscribe: subscribeExternal,\n unsubscribe: (callback: (state: Store) => void) => {\n observers.delete(callback);\n },\n broadcast: (state: Store) => {\n observers.forEach((callback) => callback(state));\n },\n } as const;\n\n /**\n * This is the hook that will be used to access the store from any component\n * in the react component tree.\n * @param selector A function that will be used to select the part of the store\n * that is needed by the component.\n * @param options Options to customize the behavior of the hook.\n * @returns An object with the selected state, a function to update the store\n * and a function to get the entire store.\n * @example\n * ```tsx\n * const { get, set } = useStore(store => store.user);\n * const { get, set } = useStore(store => store.user, { deepEqual: false });\n * const { get, set } = useStore(store => store.user, {\n * compare: (first, second) => first.id === second.id\n * });\n * ```\n */\n function useStore<SelectorOutput = Store>(\n selector: (store: Store) => SelectorOutput = (store) =>\n store as unknown as SelectorOutput,\n compare: SelectorOptions<SelectorOutput> = options.compare ?? shallowEqual\n ): UseStoreReturnType<Store, SelectorOutput> {\n const store = useContext(StoreContext);\n const lastSelectedState = useRef<SelectorOutput | undefined>(undefined);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const state = useSyncExternalStore(\n store.subscribe,\n () => {\n const selectedState = selector(store.get());\n\n if (\n lastSelectedState.current === undefined ||\n !compare(lastSelectedState.current, selectedState)\n ) {\n lastSelectedState.current = selectedState;\n }\n\n return lastSelectedState.current;\n },\n () => selector(initialState)\n );\n\n return {\n state,\n get: () => state,\n set: store.set,\n selector: () => store.get(),\n };\n }\n\n function useActions() {\n const store = useContext(StoreContext);\n\n if (!store) {\n throw new Error(\n `[${LIB_NAME}] Store not found. Make sure the component is wrapped in a ${Provider} component.`\n );\n }\n\n const actionProxy = useMemo(\n () =>\n new Proxy(actions, {\n get: (target, prop) => {\n const action = target[prop as string];\n\n if (action) {\n return (args: Parameters<typeof action>[\"1\"]) => {\n action(\n {\n set: store.set,\n get: store.get,\n },\n {\n payload: args,\n }\n );\n };\n }\n },\n set: () => {\n throw new Error(`[${LIB_NAME}] Actions cannot be updated`);\n },\n }),\n []\n );\n\n return actionProxy as unknown as ExtractActionKeys<typeof actions>;\n }\n\n const useSetStore = () => useStore(() => false).set;\n const useStoreSelector = () => useStore(() => false).selector;\n\n return {\n Provider,\n useStore,\n useSetStore,\n useActions,\n useStoreSelector,\n subscribe: observable.subscribe,\n unsubscribe: observable.unsubscribe,\n };\n}\n"],"mappings":"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","names":["__defProp","__getOwnPropDesc","__getOwnPropNames","__hasOwnProp","__copyProps","shallowEqual"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixotope/react-context-store",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "",
5
5
  "private": false,
6
6
  "main": "dist/index.js",
@@ -19,15 +19,14 @@
19
19
  "react-dom": "^19.1.1",
20
20
  "tsup": "^8.5.0",
21
21
  "typescript": "^5.7.2",
22
- "use-sync-external-store": "^1.5.0",
23
22
  "vitest": "^3.2.4",
24
- "@pixotope/utils": "0.1.0"
23
+ "@pixotope/utils": "0.2.0"
25
24
  },
26
25
  "peerDependencies": {
27
- "react": ">=17.0.0"
26
+ "react": ">=18.0.0"
28
27
  },
29
28
  "scripts": {
30
- "build": "tsup src/index.ts --format cjs,esm --dts --sourcemap --external react --external use-sync-external-store",
29
+ "build": "tsup src/index.ts --format cjs,esm --dts --sourcemap --external react",
31
30
  "check-types": "tsc --noEmit",
32
31
  "test": "vitest"
33
32
  }
@@ -7,7 +7,7 @@ import {
7
7
  import React from "react";
8
8
  import { vi, describe, test, beforeEach, it, expect } from "vitest";
9
9
 
10
- import { createContextStore } from "./store";
10
+ import { createContextStore, ActionablePayload } from "./store";
11
11
 
12
12
  const initialState = {
13
13
  count: 0,
@@ -45,7 +45,7 @@ describe("createContextStore", () => {
45
45
  store = createContextStore(initialState);
46
46
  });
47
47
 
48
- it.concurrent("should return the initial state", async () => {
48
+ it("should return the initial state", async () => {
49
49
  const { result } = renderHookWithProvider(
50
50
  () => store.useStore(),
51
51
  store.Provider
@@ -53,7 +53,7 @@ describe("createContextStore", () => {
53
53
 
54
54
  expect(result.current.get()).toEqual(initialState);
55
55
  });
56
- it.concurrent("should return the updated state", async () => {
56
+ it("should return the updated state", async () => {
57
57
  const { result } = renderHookWithProvider(
58
58
  () => store.useStore(),
59
59
  store.Provider
@@ -64,7 +64,7 @@ describe("createContextStore", () => {
64
64
 
65
65
  expect(result.current.get().count).toEqual(1);
66
66
  });
67
- it.concurrent(
67
+ it(
68
68
  "should return the reference to the same object if not changed",
69
69
  () => {
70
70
  const { result } = renderHookWithProvider(
@@ -89,7 +89,7 @@ describe("createContextStore", () => {
89
89
  expect(prevUser).toBe(userAfterCountChange);
90
90
  }
91
91
  );
92
- it.concurrent("should call compare function passed to useStore", () => {
92
+ it("should call compare function passed to useStore", () => {
93
93
  const compare = vi.fn(() => true);
94
94
  const { result } = renderHookWithProvider(
95
95
  () => store.useStore((store) => store, compare),
@@ -107,7 +107,7 @@ describe("createContextStore", () => {
107
107
 
108
108
  expect(compare).toBeCalled();
109
109
  });
110
- it.concurrent("should cause re-render if compare returns false", () => {
110
+ it("should cause re-render if compare returns false", () => {
111
111
  const compare = vi.fn(() => false);
112
112
  const { result } = renderHookWithProvider(
113
113
  () => store.useStore((store) => store, compare),
@@ -126,7 +126,7 @@ describe("createContextStore", () => {
126
126
  expect(compare).toBeCalled();
127
127
  expect(result.current.get().user.name).toEqual("Micheal");
128
128
  });
129
- it.concurrent("should not cause re-render if compare returns true", () => {
129
+ it("should not cause re-render if compare returns true", () => {
130
130
  const compare = vi.fn(() => true);
131
131
  const { result } = renderHookWithProvider(
132
132
  () => store.useStore((store) => store, compare),
@@ -145,12 +145,12 @@ describe("createContextStore", () => {
145
145
 
146
146
  expect(result.current.get().user).toEqual(prevUser);
147
147
  });
148
- test.concurrent("selector is called with the current state", () => {
148
+ test("selector is called with the current state", () => {
149
149
  const selector = vi.fn((state) => state.user);
150
150
  renderHookWithProvider(() => store.useStore(selector), store.Provider);
151
151
  expect(selector).toBeCalledWith(initialState);
152
152
  });
153
- test.concurrent(
153
+ test(
154
154
  "selector is called with the current state after update",
155
155
  () => {
156
156
  const selector = vi.fn((state) => state.user);
@@ -176,7 +176,7 @@ describe("createContextStore", () => {
176
176
  });
177
177
  }
178
178
  );
179
- test.concurrent("selector passed to userStore returns correct value", () => {
179
+ test("selector passed to userStore returns correct value", () => {
180
180
  const selector = vi.fn((state: typeof initialState) => state.user);
181
181
  const { result } = renderHookWithProvider(
182
182
  () => store.useStore(selector),
@@ -184,7 +184,7 @@ describe("createContextStore", () => {
184
184
  );
185
185
  expect(result.current.get()).toEqual(initialState.user);
186
186
  });
187
- test.concurrent(
187
+ test(
188
188
  "hook returns a selctor function that returns the correct value",
189
189
  () => {
190
190
  const { result } = renderHookWithProvider(
@@ -194,7 +194,7 @@ describe("createContextStore", () => {
194
194
  expect(result.current.selector()).toEqual(initialState);
195
195
  }
196
196
  );
197
- test.concurrent(
197
+ test(
198
198
  "hook returns a selctor function that returns the correct value after update",
199
199
  () => {
200
200
  const { result } = renderHookWithProvider(
@@ -219,7 +219,7 @@ describe("createContextStore", () => {
219
219
  });
220
220
  }
221
221
  );
222
- it.concurrent("should only cause re-render if selected value changes", () => {
222
+ it("should only cause re-render if selected value changes", () => {
223
223
  const selector = vi.fn((state: typeof initialState) => state.user);
224
224
  const { result } = renderHookWithProvider(
225
225
  () => store.useStore(selector),
@@ -235,4 +235,66 @@ describe("createContextStore", () => {
235
235
 
236
236
  expect(result.current.get()).toBe(prevUser);
237
237
  });
238
+
239
+ it("should call action without payload", () => {
240
+ const storeWithActions = createContextStore(initialState, {
241
+ increment: ({ set, get }) => {
242
+ set((prev) => ({ ...prev, count: get().count + 1 }));
243
+ },
244
+ });
245
+
246
+ const { result } = renderHookWithProvider(
247
+ () => ({
248
+ store: storeWithActions.useStore(),
249
+ actions: storeWithActions.useActions(),
250
+ }),
251
+ storeWithActions.Provider
252
+ );
253
+
254
+ act(() => {
255
+ result.current.actions.increment();
256
+ });
257
+
258
+ expect(result.current.store.get().count).toBe(1);
259
+ });
260
+
261
+ it("should call action with typed payload", () => {
262
+ const storeWithActions = createContextStore(initialState, {
263
+ incrementBy: ({ set, get }, action: ActionablePayload<number>) => {
264
+ set((prev) => ({ ...prev, count: get().count + action.payload }));
265
+ },
266
+ updateUser: (
267
+ { set },
268
+ action: ActionablePayload<{ name: string; age: number }>
269
+ ) => {
270
+ set((prev) => ({ ...prev, user: action.payload }));
271
+ },
272
+ });
273
+
274
+ const { result } = renderHookWithProvider(
275
+ () => ({
276
+ store: storeWithActions.useStore(),
277
+ actions: storeWithActions.useActions(),
278
+ }),
279
+ storeWithActions.Provider
280
+ );
281
+
282
+ act(() => {
283
+ result.current.actions.incrementBy(5);
284
+ });
285
+ expect(result.current.store.get().count).toBe(5);
286
+
287
+ act(() => {
288
+ result.current.actions.updateUser({ name: "Jane", age: 25 });
289
+ });
290
+ expect(result.current.store.get().user).toEqual({ name: "Jane", age: 25 });
291
+
292
+ // Type assertions to verify action signatures are correctly inferred
293
+ const actions = result.current.actions;
294
+ const _incrementBy: (payload: number) => void = actions.incrementBy;
295
+ const _updateUser: (payload: { name: string; age: number }) => void =
296
+ actions.updateUser;
297
+ void _incrementBy;
298
+ void _updateUser;
299
+ });
238
300
  });
package/src/store.tsx CHANGED
@@ -4,9 +4,9 @@ import React, {
4
4
  useContext,
5
5
  useMemo,
6
6
  useRef,
7
+ useSyncExternalStore,
7
8
  } from "react";
8
9
  import { shallowEqual } from "@pixotope/utils/comparison";
9
- import { useSyncExternalStore } from "use-sync-external-store/shim";
10
10
 
11
11
  const LIB_NAME = "@pixotope/react-context-store";
12
12
 
@@ -78,6 +78,7 @@ type ContextReturnType<Store, A extends Actions<Store>> = {
78
78
  ) => UseStoreReturnType<Store, SelectorOutput>;
79
79
  useActions: () => Prettify<ExtractActionKeys<A>>;
80
80
  useSetStore: () => (value: SetterArgs<Store>) => void;
81
+ useStoreSelector: () => () => Store;
81
82
  subscribe: <SelectorOutput = Store>(
82
83
  selector: (store: Store) => SelectorOutput,
83
84
  callback: (state: SelectorOutput) => void,
@@ -90,16 +91,16 @@ function isFunction(value: any): value is (prev: any) => any {
90
91
  return typeof value === "function";
91
92
  }
92
93
 
93
- export function createContextStore<Store, A extends Actions<Store> = {}>(
94
+ export function createContextStore<Store, A extends Actions<Store>>(
94
95
  ...args: Extract<A, { payload: A }> extends { payload: infer Payload }
95
96
  ? [initialState: Store, actions?: A, options?: ContextOptions]
96
97
  : [initialState: Store, options?: ContextOptions]
97
98
  ): ContextReturnType<Store, A>;
98
- export function createContextStore<Store, A extends Actions<Store> = {}>(
99
+ export function createContextStore<Store, A extends Actions<Store>>(
99
100
  initialState: Store,
100
101
  actions: A = {} as A,
101
102
  options: ContextOptions = {}
102
- ) {
103
+ ): ContextReturnType<Store, A> {
103
104
  let globalStore: Store | undefined = options.global
104
105
  ? initialState
105
106
  : undefined;
@@ -314,12 +315,14 @@ export function createContextStore<Store, A extends Actions<Store> = {}>(
314
315
  }
315
316
 
316
317
  const useSetStore = () => useStore(() => false).set;
318
+ const useStoreSelector = () => useStore(() => false).selector;
317
319
 
318
320
  return {
319
321
  Provider,
320
322
  useStore,
321
323
  useSetStore,
322
324
  useActions,
325
+ useStoreSelector,
323
326
  subscribe: observable.subscribe,
324
327
  unsubscribe: observable.unsubscribe,
325
328
  };