@pixotope/react-context-store 0.3.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.
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-check-types.log +1 -1
- package/.turbo/turbo-test.log +5 -5
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/store.test.tsx +75 -13
- package/src/store.tsx +6 -3
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pixotope/react-context-store@0.
|
|
2
|
+
> @pixotope/react-context-store@0.4.0 build /home/circleci/project/packages/react-context-store
|
|
3
3
|
> tsup src/index.ts --format cjs,esm --dts --sourcemap --external react
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
11
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m8.57 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m16.37 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 68ms
|
|
14
|
+
[32mESM[39m [1mdist/index.js [22m[32m7.96 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m16.25 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 69ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[32m2.
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 3560ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m2.34 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.34 KB[39m
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
|
|
2
|
-
> @pixotope/react-context-store@0.
|
|
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
|
[1m[46m RUN [49m[22m [36mv3.2.4 [39m[90m/home/circleci/project/packages/react-context-store[39m
|
|
7
7
|
|
|
8
|
-
[32m✓[39m src/store.test.tsx [2m([22m[
|
|
8
|
+
[32m✓[39m src/store.test.tsx [2m([22m[2m14 tests[22m[2m)[22m[32m 163[2mms[22m[39m
|
|
9
9
|
|
|
10
10
|
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
11
|
-
[2m Tests [22m [1m[
|
|
12
|
-
[2m Start at [22m 17:
|
|
13
|
-
[2m Duration [22m
|
|
11
|
+
[2m Tests [22m [1m[32m14 passed[39m[22m[90m (14)[39m
|
|
12
|
+
[2m Start at [22m 17:40:29
|
|
13
|
+
[2m Duration [22m 7.05s[2m (transform 626ms, setup 310ms, collect 804ms, tests 163ms, environment 2.10s, prepare 1.52s)[22m
|
|
14
14
|
|
package/CHANGELOG.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -235,11 +235,13 @@ function createContextStore(initialState, actions = {}, options = {}) {
|
|
|
235
235
|
return actionProxy;
|
|
236
236
|
}
|
|
237
237
|
const useSetStore = () => useStore(() => false).set;
|
|
238
|
+
const useStoreSelector = () => useStore(() => false).selector;
|
|
238
239
|
return {
|
|
239
240
|
Provider,
|
|
240
241
|
useStore,
|
|
241
242
|
useSetStore,
|
|
242
243
|
useActions,
|
|
244
|
+
useStoreSelector,
|
|
243
245
|
subscribe: observable.subscribe,
|
|
244
246
|
unsubscribe: observable.unsubscribe
|
|
245
247
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -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 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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA,sCAAAA,UAAAC,SAAA;AAAA;AACA,QAAIC,aAAY,OAAO;AACvB,QAAIC,oBAAmB,OAAO;AAC9B,QAAIC,qBAAoB,OAAO;AAC/B,QAAIC,gBAAe,OAAO,UAAU;AACpC,QAAIC,YAAW,CAAC,QAAQ,QAAQ;AAC9B,eAAS,QAAQ;AACf,QAAAJ,WAAU,QAAQ,MAAM,EAAE,KAAK,IAAI,IAAI,GAAG,YAAY,KAAK,CAAC;AAAA,IAChE;AACA,QAAIK,eAAc,CAAC,IAAI,MAAM,QAAQ,SAAS;AAC5C,UAAI,QAAQ,OAAO,SAAS,YAAY,OAAO,SAAS,YAAY;AAClE,iBAAS,OAAOH,mBAAkB,IAAI;AACpC,cAAI,CAACC,cAAa,KAAK,IAAI,GAAG,KAAK,QAAQ;AACzC,YAAAH,WAAU,IAAI,KAAK,EAAE,KAAK,MAAM,KAAK,GAAG,GAAG,YAAY,EAAE,OAAOC,kBAAiB,MAAM,GAAG,MAAM,KAAK,WAAW,CAAC;AAAA,MACvH;AACA,aAAO;AAAA,IACT;AACA,QAAIK,gBAAe,CAAC,QAAQD,aAAYL,WAAU,CAAC,GAAG,cAAc,EAAE,OAAO,KAAK,CAAC,GAAG,GAAG;AAGzF,QAAI,qBAAqB,CAAC;AAC1B,IAAAI,UAAS,oBAAoB;AAAA,MAC3B,cAAc,MAAMG;AAAA,IACtB,CAAC;AACD,IAAAR,QAAO,UAAUO,cAAa,kBAAkB;AAGhD,aAASC,cAAa,MAAM,MAAM;AAChC,UAAI,OAAO,GAAG,MAAM,IAAI,GAAG;AACzB,eAAO;AAAA,MACT;AACA,UAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,OAAO,SAAS,YAAY,SAAS,MAAM;AAC1F,eAAO;AAAA,MACT;AACA,UAAI,gBAAgB,OAAO,gBAAgB,KAAK;AAC9C,YAAI,KAAK,SAAS,KAAK,KAAM,QAAO;AACpC,mBAAW,CAAC,KAAK,KAAK,KAAK,MAAM;AAC/B,cAAI,CAAC,OAAO,GAAG,OAAO,KAAK,IAAI,GAAG,CAAC,GAAG;AACpC,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,UAAI,gBAAgB,OAAO,gBAAgB,KAAK;AAC9C,YAAI,KAAK,SAAS,KAAK,KAAM,QAAO;AACpC,mBAAW,SAAS,MAAM;AACxB,cAAI,CAAC,KAAK,IAAI,KAAK,GAAG;AACpB,mBAAO;AAAA,UACT;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,YAAM,QAAQ,OAAO,KAAK,IAAI;AAC9B,UAAI,MAAM,WAAW,OAAO,KAAK,IAAI,EAAE,QAAQ;AAC7C,eAAO;AAAA,MACT;AACA,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAI,CAAC,OAAO,UAAU,eAAe,KAAK,MAAM,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,MAAM,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG;AACvG,iBAAO;AAAA,QACT;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA;AAAA;;;AC9DA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAOO;AACP,wBAA6B;AAE7B,IAAM,WAAW;AA+EjB,SAAS,WAAW,OAAyC;AAC3D,SAAO,OAAO,UAAU;AAC1B;AAOO,SAAS,mBACd,cACA,UAAa,CAAC,GACd,UAA0B,CAAC,GACE;AAC7B,MAAI,cAAiC,QAAQ,SACzC,eACA;AAEJ,WAAS,aAAa;AAAA,IACpB,oBAAoB,CAAC;AAAA,EACvB,IAEI,CAAC,GAIH;AACA,UAAM,YAAQ,qBAAc,oCAAe,YAAY;AAEvD,UAAM,UAAM,0BAAY,MAAM,MAAM,SAAS,CAAC,CAAC;AAE/C,UAAM,kBAAc;AAAA,MAClB,IAAI,IAA4B,iBAAiB;AAAA,IACnD;AAEA,UAAM,UAAM,0BAAY,CAAC,UAA6B;AACpD,YAAM,UAAU,WAAW,KAAK,IAAI,MAAM,MAAM,OAAO,IAAI;AAE3D,UAAI,QAAQ,QAAQ;AAClB,sBAAc,MAAM;AAAA,MACtB;AAEA,kBAAY,QAAQ,QAAQ,CAAC,aAAa,SAAS,MAAM,OAAO,CAAC;AAAA,IACnE,GAAG,CAAC,CAAC;AAEL,UAAM,gBAAY,0BAAY,CAAC,aAAqC;AAClE,kBAAY,QAAQ,IAAI,QAAQ;AAEhC,aAAO,MAAM,YAAY,QAAQ,OAAO,QAAQ;AAAA,IAClD,GAAG,CAAC,CAAC;AAEL,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAIA,QAAM,mBAAe,4BAA6C,IAAI;AAMtE,WAAS,SAAS,EAAE,SAAS,GAA4B;AACvD,WACE,6BAAAC,QAAA;AAAA,MAAC,aAAa;AAAA,MAAb;AAAA,QACC,OAAO,aAAa,EAAE,mBAAmB,CAAC,WAAW,SAAS,EAAE,CAAC;AAAA;AAAA,MAEhE;AAAA,IACH;AAAA,EAEJ;AAQA,QAAM,YAAY,oBAAI,IAA4B;AAElD,WAAS,kBACP,WAA6C,CAAC,UAC5C,OACF,UACA,UAA2C,gCAC/B;AACZ,QAAI;AAEJ,UAAM,oBAAoB,CAAC,qBAAqC;AAC9D,UACE,sBAAsB,UACtB,CAAC,QAAQ,mBAAmB,gBAAgB,GAC5C;AACA,4BAAoB;AAEpB,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,CAAC,UAAiB;AACnC,YAAM,WAAW,SAAS,KAAK;AAC/B,YAAM,aAAa,kBAAkB,QAAQ;AAE7C,UAAI,YAAY;AACd,iBAAS,SAAS,KAAK,CAAC;AAAA,MAC1B;AAAA,IACF;AACA,cAAU,IAAI,UAAU;AAExB,WAAO,MAAM,UAAU,OAAO,UAAU;AAAA,EAC1C;AAMA,QAAM,aAAa;AAAA,IACjB,WAAW;AAAA,IACX,aAAa,CAAC,aAAqC;AACjD,gBAAU,OAAO,QAAQ;AAAA,IAC3B;AAAA,IACA,WAAW,CAAC,UAAiB;AAC3B,gBAAU,QAAQ,CAAC,aAAa,SAAS,KAAK,CAAC;AAAA,IACjD;AAAA,EACF;AAmBA,WAAS,SACP,WAA6C,CAAC,UAC5C,OACF,WAA2C,sBAAQ,YAAR,YAAmB,mCACnB;AAC3C,UAAM,YAAQ,yBAAW,YAAY;AACrC,UAAM,wBAAoB,qBAAmC,MAAS;AAEtE,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,IAAI,QAAQ,8DAA8D,QAAQ;AAAA,MACpF;AAAA,IACF;AAEA,UAAM,YAAQ;AAAA,MACZ,MAAM;AAAA,MACN,MAAM;AACJ,cAAM,gBAAgB,SAAS,MAAM,IAAI,CAAC;AAE1C,YACE,kBAAkB,YAAY,UAC9B,CAAC,QAAQ,kBAAkB,SAAS,aAAa,GACjD;AACA,4BAAkB,UAAU;AAAA,QAC9B;AAEA,eAAO,kBAAkB;AAAA,MAC3B;AAAA,MACA,MAAM,SAAS,YAAY;AAAA,IAC7B;AAEA,WAAO;AAAA,MACL;AAAA,MACA,KAAK,MAAM;AAAA,MACX,KAAK,MAAM;AAAA,MACX,UAAU,MAAM,MAAM,IAAI;AAAA,IAC5B;AAAA,EACF;AAEA,WAAS,aAAa;AACpB,UAAM,YAAQ,yBAAW,YAAY;AAErC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,IAAI,QAAQ,8DAA8D,QAAQ;AAAA,MACpF;AAAA,IACF;AAEA,UAAM,kBAAc;AAAA,MAClB,MACE,IAAI,MAAM,SAAS;AAAA,QACjB,KAAK,CAAC,QAAQ,SAAS;AACrB,gBAAM,SAAS,OAAO,IAAc;AAEpC,cAAI,QAAQ;AACV,mBAAO,CAAC,SAAyC;AAC/C;AAAA,gBACE;AAAA,kBACE,KAAK,MAAM;AAAA,kBACX,KAAK,MAAM;AAAA,gBACb;AAAA,gBACA;AAAA,kBACE,SAAS;AAAA,gBACX;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,QACA,KAAK,MAAM;AACT,gBAAM,IAAI,MAAM,IAAI,QAAQ,6BAA6B;AAAA,QAC3D;AAAA,MACF,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,MAAM,SAAS,MAAM,KAAK,EAAE;AAChD,QAAM,mBAAmB,MAAM,SAAS,MAAM,KAAK,EAAE;AAErD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,WAAW;AAAA,IACtB,aAAa,WAAW;AAAA,EAC1B;AACF;","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
|
|
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
|
|
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
|
@@ -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 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 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.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"tsup": "^8.5.0",
|
|
21
21
|
"typescript": "^5.7.2",
|
|
22
22
|
"vitest": "^3.2.4",
|
|
23
|
-
"@pixotope/utils": "0.
|
|
23
|
+
"@pixotope/utils": "0.2.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": ">=18.0.0"
|
package/src/store.test.tsx
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
@@ -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
|
};
|