react-redux-cache 0.0.1 → 0.0.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # react-redux-cache
2
2
 
3
- **Powerfull** and **customizable** data fetching and caching library that supports **normalization** (unlike `react-query` and `rtk-query`), built on top of `redux`.
3
+ **Powerful** yet **lightweight** data fetching and caching library that supports **normalization** unlike `react-query` and `rtk-query`, while having similar interface. Built on top of `redux`.
4
4
 
5
5
  **Normalization** is the only way to keep the state of the app **consistent** between different views, reduces the number of fetches and allows to show cached data when navigating, which greatly improves **user experience**.
6
6
 
@@ -65,7 +65,7 @@ For normalization `normalizr` package is used in this example, but any other too
65
65
  Perfect implementation is when the backend already returns normalized data.
66
66
  ```typescript
67
67
  export const getUser = async (id: number) => {
68
- const result: User = await ...
68
+ const result = await ...
69
69
 
70
70
  const normalizedResult: {
71
71
  result: number
@@ -105,13 +105,13 @@ export const UserScreen = () => {
105
105
 
106
106
  // This selector is created by createCache and also returns proper types - User and Bank
107
107
  const user = useSelectEntityById(userId, 'users')
108
- const bank = useSelectEntityById(user?.bank, 'banks')
108
+ const bank = useSelectEntityById(user?.bankId, 'banks')
109
109
 
110
110
  if (loading) {
111
- return ... // loading state
111
+ return ...
112
112
  }
113
113
 
114
- return ... // loaded state
114
+ return ...
115
115
  }
116
116
  ```
117
117
 
@@ -0,0 +1,71 @@
1
+ import { mergeEntityChanges, setMutationStateAndEntities, setQueryStateAndEntities } from './reducer';
2
+ import { Cache, EntitiesMap, Key, OptionalPartial, Typenames } from './types';
3
+ import { useMutation } from './useMutation';
4
+ import { useQuery } from './useQuery';
5
+ /**
6
+ * Creates reducer, actions and hooks for managing queries and mutations through redux cache.
7
+ */
8
+ export declare const createCache: <T extends Typenames, QP, QR, MP, MR>(cache: OptionalPartial<Cache<T, QP, QR, MP, MR>, "options">) => {
9
+ cache: Cache<T, QP, QR, MP, MR>;
10
+ /** Reducer of the cache, should be added to redux store. */
11
+ reducer: (state: {
12
+ entities: EntitiesMap<T>;
13
+ queries: { [QK in keyof QR]: import("./types").Dict<import("./types").QueryMutationState<QR[QK]>>; };
14
+ mutations: { [MK in keyof MR]: import("./types").QueryMutationState<MR[MK]>; };
15
+ } | undefined, action: {
16
+ type: `${string}MERGE_ENTITY_CHANGES`;
17
+ changes: import("./types").EntityChanges<T>;
18
+ } | {
19
+ type: `${string}SET_QUERY_STATE_AND_ENTITIES`;
20
+ queryKey: keyof QR;
21
+ queryCacheKey: Key;
22
+ state: Partial<import("./types").QueryMutationState<QR[keyof QR]>> | undefined;
23
+ entityChagnes: import("./types").EntityChanges<T> | undefined;
24
+ } | {
25
+ type: `${string}SET_MUTATION_STATE_AND_ENTITIES`;
26
+ mutationKey: keyof MR;
27
+ state: Partial<import("./types").QueryMutationState<MR[keyof MR]>> | undefined;
28
+ entityChagnes: import("./types").EntityChanges<T> | undefined;
29
+ }) => {
30
+ entities: EntitiesMap<T>;
31
+ queries: { [QK in keyof QR]: import("./types").Dict<import("./types").QueryMutationState<QR[QK]>>; };
32
+ mutations: { [MK in keyof MR]: import("./types").QueryMutationState<MR[MK]>; };
33
+ };
34
+ actions: {
35
+ /** Updates query state, and optionally merges entity changes in a single action. */
36
+ setQueryStateAndEntities: <K extends keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<import("./types").QueryMutationState<QR[K]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined) => {
37
+ type: `${string}SET_QUERY_STATE_AND_ENTITIES`;
38
+ queryKey: K;
39
+ queryCacheKey: Key;
40
+ state: Partial<import("./types").QueryMutationState<QR[K]>> | undefined;
41
+ entityChagnes: import("./types").EntityChanges<T> | undefined;
42
+ };
43
+ /** Updates mutation state, and optionally merges entity changes in a single action. */
44
+ setMutationStateAndEntities: <K_1 extends keyof MR>(mutationKey: K_1, state?: Partial<import("./types").QueryMutationState<MR[K_1]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined) => {
45
+ type: `${string}SET_MUTATION_STATE_AND_ENTITIES`;
46
+ mutationKey: K_1;
47
+ state: Partial<import("./types").QueryMutationState<MR[K_1]>> | undefined;
48
+ entityChagnes: import("./types").EntityChanges<T> | undefined;
49
+ };
50
+ /** Merge EntityChanges to the state. */
51
+ mergeEntityChanges: (changes: import("./types").EntityChanges<T>) => {
52
+ type: `${string}MERGE_ENTITY_CHANGES`;
53
+ changes: import("./types").EntityChanges<T>;
54
+ };
55
+ };
56
+ selectors: {
57
+ entitiesSelector: (state: unknown) => EntitiesMap<T>;
58
+ entitiesByTypenameSelector: <TN extends keyof T>(typename: TN) => { [K_2 in keyof T]: (state: unknown) => EntitiesMap<T>[K_2]; }[TN];
59
+ };
60
+ hooks: {
61
+ /** Fetches query when params change and subscribes to query state. */
62
+ useQuery: <QK_1 extends keyof QP | keyof QR>(options: import("./types").UseQueryOptions<T, QP, QR, MP, MR, QK_1>) => readonly [import("./types").QueryMutationState<QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never>, (params?: (QK_1 extends keyof QP & keyof QR ? QP[QK_1] : never) | undefined) => Promise<void>];
63
+ /** Subscribes to provided mutation state and provides mutate function. */
64
+ useMutation: <MK_1 extends keyof MP | keyof MR>(options: {
65
+ mutation: MK_1;
66
+ cacheOptions?: import("./types").MutationCacheOptions | undefined;
67
+ }) => readonly [(params: MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never) => Promise<void>, import("./types").QueryMutationState<MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never>, AbortController | undefined];
68
+ /** Selects entity by id and subscribes to the changes. */
69
+ useSelectEntityById: <K_3 extends keyof T>(id: Key | null | undefined, typename: K_3) => T[K_3] | undefined;
70
+ };
71
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCache = void 0;
4
+ const react_redux_1 = require("react-redux");
5
+ const reducer_1 = require("./reducer");
6
+ const useMutation_1 = require("./useMutation");
7
+ const useQuery_1 = require("./useQuery");
8
+ const utilsAndConstants_1 = require("./utilsAndConstants");
9
+ /**
10
+ * Creates reducer, actions and hooks for managing queries and mutations through redux cache.
11
+ */
12
+ const createCache = (cache) => {
13
+ var _a, _b, _c, _d;
14
+ var _e, _f, _g;
15
+ // @ts-expect-error hot
16
+ const hotReloadEnabled = Boolean(module === null || module === void 0 ? void 0 : module.hot);
17
+ // provide all optional fields
18
+ (_a = cache.options) !== null && _a !== void 0 ? _a : (cache.options = {});
19
+ (_b = (_e = cache.options).logsEnabled) !== null && _b !== void 0 ? _b : (_e.logsEnabled = false);
20
+ (_c = (_f = cache.options).validateFunctionArguments) !== null && _c !== void 0 ? _c : (_f.validateFunctionArguments = utilsAndConstants_1.isDev);
21
+ (_d = (_g = cache.options).validateHookArguments) !== null && _d !== void 0 ? _d : (_g.validateHookArguments = utilsAndConstants_1.isDev && !hotReloadEnabled);
22
+ const nonPartialCache = cache;
23
+ // make selectors
24
+ const entitiesSelector = (state) => {
25
+ return nonPartialCache.cacheStateSelector(state).entities;
26
+ };
27
+ const enitityMapSelectorByTypename = Object.keys(cache.typenames).reduce((result, x) => {
28
+ result[x] = (state) => nonPartialCache.cacheStateSelector(state).entities[x];
29
+ return result;
30
+ }, {});
31
+ return {
32
+ cache: nonPartialCache,
33
+ /** Reducer of the cache, should be added to redux store. */
34
+ reducer: (0, reducer_1.createCacheReducer)(nonPartialCache.typenames, nonPartialCache.queries, nonPartialCache.mutations, nonPartialCache.options),
35
+ actions: {
36
+ /** Updates query state, and optionally merges entity changes in a single action. */
37
+ setQueryStateAndEntities: reducer_1.setQueryStateAndEntities,
38
+ /** Updates mutation state, and optionally merges entity changes in a single action. */
39
+ setMutationStateAndEntities: reducer_1.setMutationStateAndEntities,
40
+ /** Merge EntityChanges to the state. */
41
+ mergeEntityChanges: reducer_1.mergeEntityChanges,
42
+ },
43
+ selectors: {
44
+ entitiesSelector,
45
+ entitiesByTypenameSelector: (typename) => {
46
+ return enitityMapSelectorByTypename[typename];
47
+ },
48
+ },
49
+ hooks: {
50
+ /** Fetches query when params change and subscribes to query state. */
51
+ useQuery: (options) => (0, useQuery_1.useQuery)(nonPartialCache, options),
52
+ /** Subscribes to provided mutation state and provides mutate function. */
53
+ useMutation: (options) => (0, useMutation_1.useMutation)(nonPartialCache, options),
54
+ /** Selects entity by id and subscribes to the changes. */
55
+ useSelectEntityById: (id, typename) => {
56
+ return (0, react_redux_1.useSelector)((state) => id == null ? undefined : nonPartialCache.cacheStateSelector(state).entities[typename][id]);
57
+ },
58
+ },
59
+ };
60
+ };
61
+ exports.createCache = createCache;
package/dist/index.d.ts CHANGED
@@ -1,74 +1,6 @@
1
- import { mergeEntityChanges, setMutationStateAndEntities, setQueryStateAndEntities } from './reducer';
2
- import { Cache, EntitiesMap, Key, OptionalPartial, Typenames } from './types';
3
- import { useMutation } from './useMutation';
4
- import { useQuery } from './useQuery';
5
- export * from './reducer';
1
+ export { createCache } from './createCache';
2
+ export type { ReduxCacheState } from './reducer';
6
3
  export * from './types';
7
- export * from './useMutation';
8
- export * from './useQuery';
9
- export * from './utilsAndConstants';
10
- /** Creates reducer, actions and hooks for managing queries and mutations through redux cache. */
11
- export declare const createCache: <T extends Typenames, QP, QR, MP, MR>(cache: OptionalPartial<Cache<T, QP, QR, MP, MR>, "options">) => {
12
- cache: Cache<T, QP, QR, MP, MR>;
13
- /** Reducer of the cache, should be added to redux store. */
14
- reducer: (state: {
15
- entities: EntitiesMap<T>;
16
- queries: { [QK in keyof QR]: import("./types").Dict<import("./types").QueryMutationState<QR[QK]>>; };
17
- mutations: { [MK in keyof MR]: import("./types").QueryMutationState<MR[MK]>; };
18
- } | undefined, action: {
19
- type: `${string}MERGE_ENTITY_CHANGES`;
20
- changes: import("./types").EntityChanges<T>;
21
- } | {
22
- type: `${string}SET_QUERY_STATE_AND_ENTITIES`;
23
- queryKey: keyof QR;
24
- queryCacheKey: Key;
25
- state: Partial<import("./types").QueryMutationState<QR[keyof QR]>> | undefined;
26
- entityChagnes: import("./types").EntityChanges<T> | undefined;
27
- } | {
28
- type: `${string}SET_MUTATION_STATE_AND_ENTITIES`;
29
- mutationKey: keyof MR;
30
- state: Partial<import("./types").QueryMutationState<MR[keyof MR]>> | undefined;
31
- entityChagnes: import("./types").EntityChanges<T> | undefined;
32
- }) => {
33
- entities: EntitiesMap<T>;
34
- queries: { [QK in keyof QR]: import("./types").Dict<import("./types").QueryMutationState<QR[QK]>>; };
35
- mutations: { [MK in keyof MR]: import("./types").QueryMutationState<MR[MK]>; };
36
- };
37
- actions: {
38
- /** Updates query state, and optionally merges entity changes in a single action. */
39
- setQueryStateAndEntities: <K extends keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<import("./types").QueryMutationState<QR[K]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined) => {
40
- type: `${string}SET_QUERY_STATE_AND_ENTITIES`;
41
- queryKey: K;
42
- queryCacheKey: Key;
43
- state: Partial<import("./types").QueryMutationState<QR[K]>> | undefined;
44
- entityChagnes: import("./types").EntityChanges<T> | undefined;
45
- };
46
- /** Updates mutation state, and optionally merges entity changes in a single action. */
47
- setMutationStateAndEntities: <K_1 extends keyof MR>(mutationKey: K_1, state?: Partial<import("./types").QueryMutationState<MR[K_1]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined) => {
48
- type: `${string}SET_MUTATION_STATE_AND_ENTITIES`;
49
- mutationKey: K_1;
50
- state: Partial<import("./types").QueryMutationState<MR[K_1]>> | undefined;
51
- entityChagnes: import("./types").EntityChanges<T> | undefined;
52
- };
53
- /** Merge EntityChanges to the state. */
54
- mergeEntityChanges: (changes: import("./types").EntityChanges<T>) => {
55
- type: `${string}MERGE_ENTITY_CHANGES`;
56
- changes: import("./types").EntityChanges<T>;
57
- };
58
- };
59
- selectors: {
60
- entitiesSelector: (state: unknown) => EntitiesMap<T>;
61
- entitiesByTypenameSelector: <TN extends keyof T>(typename: TN) => { [K_2 in keyof T]: (state: unknown) => EntitiesMap<T>[K_2]; }[TN];
62
- };
63
- hooks: {
64
- /** Fetches query when params change and subscribes to query state. */
65
- useQuery: <QK_1 extends keyof QP | keyof QR>(options: import("./types").UseQueryOptions<T, QP, QR, MP, MR, QK_1>) => readonly [import("./types").QueryMutationState<QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never>, (params?: (QK_1 extends keyof QP & keyof QR ? QP[QK_1] : never) | undefined) => Promise<void>];
66
- /** Subscribes to provided mutation state and provides mutate function. */
67
- useMutation: <MK_1 extends keyof MP | keyof MR>(options: {
68
- mutation: MK_1;
69
- cacheOptions?: import("./types").MutationCacheOptions | undefined;
70
- }) => readonly [(params: MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never) => Promise<void>, import("./types").QueryMutationState<MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never>, AbortController | undefined];
71
- /** Selects entity by id and subscribes to the changes. */
72
- useSelectEntityById: <K_3 extends keyof T>(id: Key | null | undefined, typename: K_3) => T[K_3] | undefined;
73
- };
74
- };
4
+ export { defaultMutationCacheOptions } from './useMutation';
5
+ export { defaultQueryCacheOptions, queryCacheOptionsByPolicy } from './useQuery';
6
+ export { defaultGetParamsKey, defaultQueryMutationState, processEntityChanges, } from './utilsAndConstants';
package/dist/index.js CHANGED
@@ -14,18 +14,26 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.createCache = void 0;
18
- const react_redux_1 = require("react-redux");
19
- const reducer_1 = require("./reducer");
20
- const useMutation_1 = require("./useMutation");
21
- const useQuery_1 = require("./useQuery");
22
- const utilsAndConstants_1 = require("./utilsAndConstants");
17
+ exports.processEntityChanges = exports.defaultQueryMutationState = exports.defaultGetParamsKey = exports.queryCacheOptionsByPolicy = exports.defaultQueryCacheOptions = exports.defaultMutationCacheOptions = exports.createCache = void 0;
18
+ var createCache_1 = require("./createCache");
19
+ Object.defineProperty(exports, "createCache", { enumerable: true, get: function () { return createCache_1.createCache; } });
20
+ __exportStar(require("./types"), exports);
21
+ var useMutation_1 = require("./useMutation");
22
+ Object.defineProperty(exports, "defaultMutationCacheOptions", { enumerable: true, get: function () { return useMutation_1.defaultMutationCacheOptions; } });
23
+ var useQuery_1 = require("./useQuery");
24
+ Object.defineProperty(exports, "defaultQueryCacheOptions", { enumerable: true, get: function () { return useQuery_1.defaultQueryCacheOptions; } });
25
+ Object.defineProperty(exports, "queryCacheOptionsByPolicy", { enumerable: true, get: function () { return useQuery_1.queryCacheOptionsByPolicy; } });
26
+ var utilsAndConstants_1 = require("./utilsAndConstants");
27
+ Object.defineProperty(exports, "defaultGetParamsKey", { enumerable: true, get: function () { return utilsAndConstants_1.defaultGetParamsKey; } });
28
+ Object.defineProperty(exports, "defaultQueryMutationState", { enumerable: true, get: function () { return utilsAndConstants_1.defaultQueryMutationState; } });
29
+ Object.defineProperty(exports, "processEntityChanges", { enumerable: true, get: function () { return utilsAndConstants_1.processEntityChanges; } });
23
30
  // Backlog
24
31
  // ! high
25
- // create package with README
32
+ // package.json exports
26
33
  // cover with tests
27
34
  // ! medium
28
- // add params to the state
35
+ // return back deserialize selector?
36
+ // resultSelector - return also boolean that result is full enough
29
37
  // selector for entities by typename
30
38
  // provide call query/mutation function to call them without hooks, but with all state updates
31
39
  // get typenames from schema? (useSelectDenormalized)
@@ -43,6 +51,7 @@ const utilsAndConstants_1 = require("./utilsAndConstants");
43
51
  // make error type generic
44
52
  // proper types, remove as, any, todo
45
53
  // ! low
54
+ // add params to the state?
46
55
  // cancellation to queries
47
56
  // if mutation & query alrady loading - make options: last, throttle, debounce, parallel?
48
57
  // add time-to-live option, and/or time-to-refresh
@@ -54,59 +63,3 @@ const utilsAndConstants_1 = require("./utilsAndConstants");
54
63
  // set options in refresh/mutate functions
55
64
  // multiple reducers instead of 1?
56
65
  // don't cache result if resultSelector set?
57
- __exportStar(require("./reducer"), exports);
58
- __exportStar(require("./types"), exports);
59
- __exportStar(require("./useMutation"), exports);
60
- __exportStar(require("./useQuery"), exports);
61
- __exportStar(require("./utilsAndConstants"), exports);
62
- /** Creates reducer, actions and hooks for managing queries and mutations through redux cache. */
63
- const createCache = (cache) => {
64
- var _a, _b, _c, _d;
65
- var _e, _f, _g;
66
- // @ts-expect-error hot
67
- const hotReloadEnabled = Boolean(module === null || module === void 0 ? void 0 : module.hot);
68
- // provide all optional fields
69
- (_a = cache.options) !== null && _a !== void 0 ? _a : (cache.options = {});
70
- (_b = (_e = cache.options).logsEnabled) !== null && _b !== void 0 ? _b : (_e.logsEnabled = false);
71
- (_c = (_f = cache.options).validateFunctionArguments) !== null && _c !== void 0 ? _c : (_f.validateFunctionArguments = utilsAndConstants_1.isDev);
72
- (_d = (_g = cache.options).validateHookArguments) !== null && _d !== void 0 ? _d : (_g.validateHookArguments = utilsAndConstants_1.isDev && !hotReloadEnabled);
73
- const nonPartialCache = cache;
74
- // make selectors
75
- const entitiesSelector = (state) => {
76
- return nonPartialCache.cacheStateSelector(state).entities;
77
- };
78
- const enitityMapSelectorByTypename = Object.keys(cache.typenames).reduce((result, x) => {
79
- result[x] = (state) => nonPartialCache.cacheStateSelector(state).entities[x];
80
- return result;
81
- }, {});
82
- return {
83
- cache: nonPartialCache,
84
- /** Reducer of the cache, should be added to redux store. */
85
- reducer: (0, reducer_1.createCacheReducer)(nonPartialCache.typenames, nonPartialCache.queries, nonPartialCache.mutations, nonPartialCache.options),
86
- actions: {
87
- /** Updates query state, and optionally merges entity changes in a single action. */
88
- setQueryStateAndEntities: reducer_1.setQueryStateAndEntities,
89
- /** Updates mutation state, and optionally merges entity changes in a single action. */
90
- setMutationStateAndEntities: reducer_1.setMutationStateAndEntities,
91
- /** Merge EntityChanges to the state. */
92
- mergeEntityChanges: reducer_1.mergeEntityChanges,
93
- },
94
- selectors: {
95
- entitiesSelector,
96
- entitiesByTypenameSelector: (typename) => {
97
- return enitityMapSelectorByTypename[typename];
98
- },
99
- },
100
- hooks: {
101
- /** Fetches query when params change and subscribes to query state. */
102
- useQuery: (options) => (0, useQuery_1.useQuery)(nonPartialCache, options),
103
- /** Subscribes to provided mutation state and provides mutate function. */
104
- useMutation: (options) => (0, useMutation_1.useMutation)(nonPartialCache, options),
105
- /** Selects entity by id and subscribes to the changes. */
106
- useSelectEntityById: (id, typename) => {
107
- return (0, react_redux_1.useSelector)((state) => id == null ? undefined : nonPartialCache.cacheStateSelector(state).entities[typename][id]);
108
- },
109
- },
110
- };
111
- };
112
- exports.createCache = createCache;
package/dist/reducer.js CHANGED
@@ -26,7 +26,7 @@ const createCacheReducer = (typenames, queries, mutations, cacheOptions) => {
26
26
  });
27
27
  return (state = initialState, action) => {
28
28
  switch (action.type) {
29
- case '@RRQN/SET_QUERY_STATE_AND_ENTITIES': {
29
+ case '@RRC/SET_QUERY_STATE_AND_ENTITIES': {
30
30
  const { queryKey, queryCacheKey, state: queryState, entityChagnes } = action;
31
31
  const newEntities = entityChagnes && (0, utilsAndConstants_1.processEntityChanges)(state.entities, entityChagnes, cacheOptions);
32
32
  if (!queryState && !newEntities) {
@@ -34,7 +34,7 @@ const createCacheReducer = (typenames, queries, mutations, cacheOptions) => {
34
34
  }
35
35
  return Object.assign(Object.assign(Object.assign({}, state), (newEntities ? { entities: newEntities } : null)), { queries: Object.assign(Object.assign({}, state.queries), { [queryKey]: Object.assign(Object.assign({}, state.queries[queryKey]), { [queryCacheKey]: Object.assign(Object.assign({}, state.queries[queryKey][queryCacheKey]), queryState) }) }) });
36
36
  }
37
- case '@RRQN/SET_MUTATION_STATE_AND_ENTITIES': {
37
+ case '@RRC/SET_MUTATION_STATE_AND_ENTITIES': {
38
38
  const { mutationKey, state: mutationState, entityChagnes } = action;
39
39
  const newEntities = entityChagnes && (0, utilsAndConstants_1.processEntityChanges)(state.entities, entityChagnes, cacheOptions);
40
40
  if (!mutationState && !newEntities) {
@@ -42,7 +42,7 @@ const createCacheReducer = (typenames, queries, mutations, cacheOptions) => {
42
42
  }
43
43
  return Object.assign(Object.assign(Object.assign({}, state), (newEntities ? { entities: newEntities } : null)), { mutations: Object.assign(Object.assign({}, state.mutations), { [mutationKey]: Object.assign(Object.assign({}, state.mutations[mutationKey]), mutationState) }) });
44
44
  }
45
- case '@RRQN/MERGE_ENTITY_CHANGES': {
45
+ case '@RRC/MERGE_ENTITY_CHANGES': {
46
46
  const { changes } = action;
47
47
  const newEntities = (0, utilsAndConstants_1.processEntityChanges)(state.entities, changes, cacheOptions);
48
48
  return newEntities ? Object.assign(Object.assign({}, state), { entities: newEntities }) : state;
package/dist/types.d.ts CHANGED
@@ -7,11 +7,11 @@ export type OptionalPartial<T, K extends keyof T> = Partial<{
7
7
  /** Entity changes to be merged to redux state. */
8
8
  export type EntityChanges<T extends Typenames> = {
9
9
  /** Entities that will be merged with existing. */
10
- merge?: Partial<PartialEntitiesMap<T>>;
10
+ merge?: PartialEntitiesMap<T>;
11
11
  /** Entities that will replace existing. */
12
12
  replace?: Partial<EntitiesMap<T>>;
13
- /** Ids of entities that will be removed. */
14
- remove?: Partial<EntityIds<T>>;
13
+ /** Ids of entities that will be removed. */
14
+ remove?: EntityIds<T>;
15
15
  /** Alias for `merge` to support normalizr. */
16
16
  entities?: EntityChanges<T>['merge'];
17
17
  };
@@ -48,13 +48,13 @@ export type CacheOptions = {
48
48
  logsEnabled: boolean;
49
49
  };
50
50
  export type PartialEntitiesMap<T extends Typenames> = {
51
- [K in keyof T]: Dict<Partial<T[K]>>;
51
+ [K in keyof T]?: Dict<Partial<T[K]>>;
52
52
  };
53
53
  export type EntitiesMap<T extends Typenames> = {
54
- [K in keyof T]: Dict<T[K] | undefined>;
54
+ [K in keyof T]: Dict<T[K]>;
55
55
  };
56
56
  export type EntityIds<T extends Typenames> = {
57
- [K in keyof T]: Key[];
57
+ [K in keyof T]?: Key[];
58
58
  };
59
59
  export type Query<T extends Typenames, P, R> = (params: P) => Promise<QueryResponse<T, R>>;
60
60
  export type QueryInfo<T extends Typenames, P, R, S> = {
@@ -77,7 +77,7 @@ export type QueryInfo<T extends Typenames, P, R, S> = {
77
77
  /**
78
78
  * Params key is used for determining if parameters were changed and fetch is needed.
79
79
  * Also used as cache key, of `getCacheKey` wasn't provided.
80
- * Default implementation uses `JSON.stringify` of parameters.
80
+ * Default implementation uses `JSON.stringify` or `String()` depending on type.
81
81
  * */
82
82
  getParamsKey?: (params?: P) => Key;
83
83
  /**
@@ -1,5 +1,5 @@
1
1
  import { Cache, MutationCacheOptions, QueryMutationState, Typenames } from './types';
2
- export declare const DEFAULT_MUTATION_CACHE_OPTIONS: MutationCacheOptions;
2
+ export declare const defaultMutationCacheOptions: MutationCacheOptions;
3
3
  export declare const useMutation: <T extends Typenames, MP, MR, MK extends keyof MP | keyof MR>(cache: Cache<T, unknown, unknown, MP, MR>, options: {
4
4
  mutation: MK;
5
5
  cacheOptions?: MutationCacheOptions | undefined;
@@ -9,18 +9,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.useMutation = exports.DEFAULT_MUTATION_CACHE_OPTIONS = void 0;
12
+ exports.useMutation = exports.defaultMutationCacheOptions = void 0;
13
13
  const react_1 = require("react");
14
14
  const react_redux_1 = require("react-redux");
15
15
  const reducer_1 = require("./reducer");
16
16
  const utilsAndConstants_1 = require("./utilsAndConstants");
17
- exports.DEFAULT_MUTATION_CACHE_OPTIONS = {
17
+ exports.defaultMutationCacheOptions = {
18
18
  cacheMutationState: true,
19
19
  cacheEntities: true,
20
20
  };
21
21
  const useMutation = (cache, options) => {
22
22
  var _a, _b;
23
- const { mutation: mutationKey, cacheOptions = (_a = cache.mutations[mutationKey].cacheOptions) !== null && _a !== void 0 ? _a : exports.DEFAULT_MUTATION_CACHE_OPTIONS, } = options;
23
+ const { mutation: mutationKey, cacheOptions = (_a = cache.mutations[mutationKey].cacheOptions) !== null && _a !== void 0 ? _a : exports.defaultMutationCacheOptions, } = options;
24
24
  const dispatch = (0, react_redux_1.useDispatch)();
25
25
  cache.options.logsEnabled &&
26
26
  (0, utilsAndConstants_1.log)('useMutation', {
@@ -54,7 +54,7 @@ const useMutation = (cache, options) => {
54
54
  // eslint-disable-next-line react-hooks/exhaustive-deps
55
55
  }, []);
56
56
  // @ts-expect-error fix later
57
- const mutationState = (_b = (0, react_redux_1.useSelector)(mutationStateSelector)) !== null && _b !== void 0 ? _b : utilsAndConstants_1.defaultEndpointState;
57
+ const mutationState = (_b = (0, react_redux_1.useSelector)(mutationStateSelector)) !== null && _b !== void 0 ? _b : utilsAndConstants_1.defaultQueryMutationState;
58
58
  const mutate = (0, react_1.useCallback)((params) => __awaiter(void 0, void 0, void 0, function* () {
59
59
  cache.options.logsEnabled &&
60
60
  (0, utilsAndConstants_1.log)('mutate', {
@@ -1,6 +1,6 @@
1
1
  import { Cache, QueryCacheOptions, QueryCachePolicy, QueryMutationState, Typenames, UseQueryOptions } from './types';
2
- export declare const QUERY_CACHE_OPTIONS_BY_POLICY: Record<QueryCachePolicy, QueryCacheOptions>;
3
- export declare const DEFAULT_QUERY_CACHE_OPTIONS: {
2
+ export declare const queryCacheOptionsByPolicy: Record<QueryCachePolicy, QueryCacheOptions>;
3
+ export declare const defaultQueryCacheOptions: {
4
4
  readonly policy: "cache-first";
5
5
  readonly cacheQueryState: true;
6
6
  readonly cacheEntities: true;
package/dist/useQuery.js CHANGED
@@ -9,30 +9,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.useQuery = exports.DEFAULT_QUERY_CACHE_OPTIONS = exports.QUERY_CACHE_OPTIONS_BY_POLICY = void 0;
12
+ exports.useQuery = exports.defaultQueryCacheOptions = exports.queryCacheOptionsByPolicy = void 0;
13
13
  const react_1 = require("react");
14
14
  const react_redux_1 = require("react-redux");
15
15
  const reducer_1 = require("./reducer");
16
16
  const utilsAndConstants_1 = require("./utilsAndConstants");
17
- const CACHE_FIRST_OPTIONS = {
17
+ const cacheFirstOptions = {
18
18
  policy: 'cache-first',
19
19
  cacheQueryState: true,
20
20
  cacheEntities: true,
21
21
  };
22
- exports.QUERY_CACHE_OPTIONS_BY_POLICY = {
23
- 'cache-first': CACHE_FIRST_OPTIONS,
24
- 'cache-and-fetch': Object.assign(Object.assign({}, CACHE_FIRST_OPTIONS), { policy: 'cache-and-fetch' }),
22
+ exports.queryCacheOptionsByPolicy = {
23
+ 'cache-first': cacheFirstOptions,
24
+ 'cache-and-fetch': Object.assign(Object.assign({}, cacheFirstOptions), { policy: 'cache-and-fetch' }),
25
25
  };
26
- exports.DEFAULT_QUERY_CACHE_OPTIONS = CACHE_FIRST_OPTIONS;
26
+ exports.defaultQueryCacheOptions = cacheFirstOptions;
27
27
  const useQuery = (cache, options) => {
28
28
  var _a, _b, _c, _d;
29
29
  const getParamsKey = (_a = cache.queries[options.query].getParamsKey) !== null && _a !== void 0 ? _a : (utilsAndConstants_1.defaultGetParamsKey);
30
- const { query: queryKey, skip, params: hookParams, cacheOptions: cacheOptionsOrPolicy = (_b = cache.queries[queryKey].cacheOptions) !== null && _b !== void 0 ? _b : exports.DEFAULT_QUERY_CACHE_OPTIONS, mergeResults = cache.queries[queryKey].mergeResults, getCacheKey = (_c = cache.queries[queryKey].getCacheKey) !== null && _c !== void 0 ? _c : getParamsKey, } = options;
30
+ const { query: queryKey, skip, params: hookParams, cacheOptions: cacheOptionsOrPolicy = (_b = cache.queries[queryKey].cacheOptions) !== null && _b !== void 0 ? _b : exports.defaultQueryCacheOptions, mergeResults = cache.queries[queryKey].mergeResults, getCacheKey = (_c = cache.queries[queryKey].getCacheKey) !== null && _c !== void 0 ? _c : getParamsKey, } = options;
31
31
  const hookParamsKey = getParamsKey(
32
32
  // @ts-expect-error fix later
33
33
  hookParams);
34
34
  const cacheOptions = typeof cacheOptionsOrPolicy === 'string'
35
- ? exports.QUERY_CACHE_OPTIONS_BY_POLICY[cacheOptionsOrPolicy]
35
+ ? exports.queryCacheOptionsByPolicy[cacheOptionsOrPolicy]
36
36
  : cacheOptionsOrPolicy;
37
37
  const store = (0, react_redux_1.useStore)();
38
38
  // Check values that should be set once.
@@ -89,7 +89,7 @@ const useQuery = (cache, options) => {
89
89
  return queryState; // TODO proper type
90
90
  // eslint-disable-next-line react-hooks/exhaustive-deps
91
91
  }, []);
92
- const queryStateFromSelector = (_d = (0, react_redux_1.useSelector)(queryStateSelector)) !== null && _d !== void 0 ? _d : utilsAndConstants_1.defaultEndpointState;
92
+ const queryStateFromSelector = (_d = (0, react_redux_1.useSelector)(queryStateSelector)) !== null && _d !== void 0 ? _d : utilsAndConstants_1.defaultQueryMutationState;
93
93
  const queryState = hasResultFromSelector
94
94
  ? (Object.assign(Object.assign({}, queryStateFromSelector), { result: resultFromSelector }))
95
95
  : queryStateFromSelector;
@@ -2,7 +2,7 @@
2
2
  import { CacheOptions, EntitiesMap, EntityChanges, Typenames } from './types';
3
3
  export declare const PACKAGE_SHORT_NAME = "RRC";
4
4
  export declare const isDev: boolean;
5
- export declare const defaultEndpointState: {
5
+ export declare const defaultQueryMutationState: {
6
6
  readonly loading: false;
7
7
  };
8
8
  export declare const defaultGetParamsKey: <P = unknown>(params: P) => string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processEntityChanges = exports.log = exports.useAssertValueNotChanged = exports.useForceUpdate = exports.defaultGetParamsKey = exports.defaultEndpointState = exports.isDev = exports.PACKAGE_SHORT_NAME = void 0;
3
+ exports.processEntityChanges = exports.log = exports.useAssertValueNotChanged = exports.useForceUpdate = exports.defaultGetParamsKey = exports.defaultQueryMutationState = exports.isDev = exports.PACKAGE_SHORT_NAME = void 0;
4
4
  const react_1 = require("react");
5
5
  const react_2 = require("react");
6
6
  exports.PACKAGE_SHORT_NAME = 'RRC';
@@ -13,8 +13,17 @@ exports.isDev = (() => {
13
13
  return process.env.NODE_ENV === 'development';
14
14
  }
15
15
  })();
16
- exports.defaultEndpointState = { loading: false };
17
- const defaultGetParamsKey = (params) => !params ? '' : JSON.stringify(params);
16
+ exports.defaultQueryMutationState = { loading: false };
17
+ const defaultGetParamsKey = (params) => {
18
+ switch (typeof params) {
19
+ case 'string':
20
+ return params;
21
+ case 'object':
22
+ return JSON.stringify(params);
23
+ default:
24
+ return String(params);
25
+ }
26
+ };
18
27
  exports.defaultGetParamsKey = defaultGetParamsKey;
19
28
  const forceUpdateReducer = (i) => i + 1;
20
29
  /**
@@ -45,35 +54,43 @@ exports.log = log;
45
54
  */
46
55
  const processEntityChanges = (entities, changes, options) => {
47
56
  var _a, _b, _c;
48
- const { merge = changes.entities, replace, remove } = changes;
49
- if (!merge && !replace && !remove) {
50
- return undefined;
51
- }
52
57
  if (options.validateFunctionArguments) {
53
58
  // check for merge and entities both set
54
59
  if (changes.merge && changes.entities) {
55
- throw new Error('Response merge and entities should not be both set');
56
- }
57
- // check for key intersection
58
- const mergeKeys = merge && Object.keys(merge);
59
- const replaceKeys = replace && Object.keys(replace);
60
- const removeKeys = remove && Object.keys(remove);
61
- const keysSet = new Set(mergeKeys);
62
- replaceKeys === null || replaceKeys === void 0 ? void 0 : replaceKeys.forEach((key) => keysSet.add(key));
63
- removeKeys === null || removeKeys === void 0 ? void 0 : removeKeys.forEach((key) => keysSet.add(key));
64
- const totalKeysInResponse = ((_a = mergeKeys === null || mergeKeys === void 0 ? void 0 : mergeKeys.length) !== null && _a !== void 0 ? _a : 0) + ((_b = replaceKeys === null || replaceKeys === void 0 ? void 0 : replaceKeys.length) !== null && _b !== void 0 ? _b : 0) + ((_c = removeKeys === null || removeKeys === void 0 ? void 0 : removeKeys.length) !== null && _c !== void 0 ? _c : 0);
65
- if (keysSet.size !== totalKeysInResponse) {
66
- throw new Error('Merge, replace and remove keys should not intersect');
60
+ throw new Error('Merge and entities should not be both set');
67
61
  }
68
62
  }
63
+ const { merge = changes.entities, replace, remove } = changes;
64
+ if (!merge && !replace && !remove) {
65
+ return undefined;
66
+ }
69
67
  let result;
70
68
  for (const typename in entities) {
71
69
  const entitiesToMerge = merge === null || merge === void 0 ? void 0 : merge[typename];
72
70
  const entitiesToReplace = replace === null || replace === void 0 ? void 0 : replace[typename];
73
71
  const entitiesToRemove = remove === null || remove === void 0 ? void 0 : remove[typename];
74
- if (!entitiesToMerge && !entitiesToReplace && !entitiesToRemove) {
72
+ if (!entitiesToMerge && !entitiesToReplace && !(entitiesToRemove === null || entitiesToRemove === void 0 ? void 0 : entitiesToRemove.length)) {
75
73
  continue;
76
74
  }
75
+ // check for key intersection
76
+ if (options.validateFunctionArguments) {
77
+ const mergeIds = entitiesToMerge && Object.keys(entitiesToMerge);
78
+ const replaceIds = entitiesToReplace && Object.keys(entitiesToReplace);
79
+ const idsSet = new Set(mergeIds);
80
+ replaceIds === null || replaceIds === void 0 ? void 0 : replaceIds.forEach((id) => idsSet.add(id));
81
+ entitiesToRemove === null || entitiesToRemove === void 0 ? void 0 : entitiesToRemove.forEach((id) => idsSet.add(String(id))); // String() because Object.keys always returns strings
82
+ const totalKeysInResponse = ((_a = mergeIds === null || mergeIds === void 0 ? void 0 : mergeIds.length) !== null && _a !== void 0 ? _a : 0) + ((_b = replaceIds === null || replaceIds === void 0 ? void 0 : replaceIds.length) !== null && _b !== void 0 ? _b : 0) + ((_c = entitiesToRemove === null || entitiesToRemove === void 0 ? void 0 : entitiesToRemove.length) !== null && _c !== void 0 ? _c : 0);
83
+ if (totalKeysInResponse !== 0 && idsSet.size !== totalKeysInResponse) {
84
+ throw new Error('Merge, replace and remove changes have intersections for: ' + typename);
85
+ }
86
+ console.log('[VALIDATe]', {
87
+ totalKeysInResponse,
88
+ idsSet,
89
+ mergeIds,
90
+ replaceIds,
91
+ entitiesToRemove,
92
+ });
93
+ }
77
94
  const newEntities = Object.assign({}, entities[typename]);
78
95
  // remove
79
96
  entitiesToRemove === null || entitiesToRemove === void 0 ? void 0 : entitiesToRemove.forEach((id) => delete newEntities[id]);
package/package.json CHANGED
@@ -2,21 +2,19 @@
2
2
  "name": "react-redux-cache",
3
3
  "author": "Alexander Danilov",
4
4
  "license": "MIT",
5
- "version": "0.0.1",
5
+ "version": "0.0.3",
6
6
  "description": "Powerful data fetching and caching library that supports normalization, built on top of redux",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "scripts": {
10
- "example": "yarn build && (cd example && yarn && yarn start)",
10
+ "example": "yarn build && (cd example && yarn cache clean && yarn add ../package.tgz && yarn start)",
11
11
  "clean": "rm -rf dist package.tgz",
12
12
  "build": "yarn clean && tsc && yarn pack -f package.tgz",
13
- "test": "yarn build && node node_modules/jest/bin/jest.js",
14
- "prepublishOnly": "yarn test"
13
+ "test": "node node_modules/jest/bin/jest.js",
14
+ "prepublishOnly": "yarn build && yarn test"
15
15
  },
16
- "files": [
17
- "dist/*"
18
- ],
19
16
  "devDependencies": {
17
+ "@testing-library/react": "14.0.0",
20
18
  "@types/jest": "29.5.1",
21
19
  "@types/node": "20.1.2",
22
20
  "@types/redux-logger": "3.0.9",
@@ -43,8 +41,11 @@
43
41
  "react-redux": "^4",
44
42
  "redux": "^4"
45
43
  },
46
- "dependencies": {
47
- "redux-persist": "6.0.0"
44
+ "files": [
45
+ "dist/*"
46
+ ],
47
+ "exports": {
48
+ ".": "./dist/index.js"
48
49
  },
49
50
  "keywords": [
50
51
  "react",
@@ -52,5 +53,9 @@
52
53
  "cache",
53
54
  "query",
54
55
  "normalization"
55
- ]
56
+ ],
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "https://github.com/gentlee/react-redux-cache.git"
60
+ }
56
61
  }