react-redux-cache 0.0.11 → 0.0.12

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
@@ -17,8 +17,11 @@ Usage example can be found in `example/` folder and run by `npm run example` com
17
17
  - [api.ts](https://github.com/gentlee/react-redux-cache#apits)
18
18
  - [Usage](https://github.com/gentlee/react-redux-cache#usage)
19
19
  - [Advanced](https://github.com/gentlee/react-redux-cache#advanced)
20
+ - [resultSelector](https://github.com/gentlee/react-redux-cache#resultselector)
20
21
  - [Infinite scroll pagination](https://github.com/gentlee/react-redux-cache#infinite-scroll-pagination)
21
22
  - [redux-persist](https://github.com/gentlee/react-redux-cache#redux-persist)
23
+ - [FAQ](https://github.com/gentlee/react-redux-cache#faq)
24
+ - [What is cache key?](https://github.com/gentlee/react-redux-cache#what-is-cache-key)
22
25
 
23
26
  ### Installation
24
27
  `react`, `redux` and `react-redux` are peer dependencies.
@@ -138,6 +141,46 @@ export const UserScreen = () => {
138
141
 
139
142
  ### Advanced
140
143
 
144
+ #### resultSelector
145
+
146
+ By default result of a query is stored under its **cache key**, but sometimes it makes sense to take result from other queries or normalized entities.
147
+
148
+ For example when single `User` entity is requested by `userId` for the first time, the entity can already be in the cache after `getUsers` query finished.
149
+
150
+ For that case `resultSelector` can be used:
151
+
152
+ ```typescript
153
+
154
+ // createCache
155
+
156
+ ... = createCache({
157
+ ...
158
+ queries: {
159
+ ...
160
+ getUser: {
161
+ query: getUser,
162
+ resultSelector: (state, id) => state.entities.users[id]?.id, // <-- Result is selected from cached entities
163
+ },
164
+ },
165
+ })
166
+
167
+ // component
168
+
169
+ export const UserScreen = () => {
170
+ ...
171
+
172
+ // When screen mounts for the first time, query is not fetched
173
+ // and cached value is returned if user entity was already in the cache
174
+ const [{result, loading, error}] = useQuery({
175
+ query: 'getUser',
176
+ params: userId,
177
+ })
178
+
179
+ ...
180
+ }
181
+
182
+ ```
183
+
141
184
  #### Infinite scroll pagination
142
185
 
143
186
  Here is an example of `getUsers` query configuration with pagination support. You can check full implementation in `/example` folder.
@@ -225,3 +268,28 @@ const persistedReducer = persistReducer(
225
268
  reducer
226
269
  )
227
270
  ```
271
+
272
+ ### FAQ
273
+
274
+ #### What is cache key?
275
+
276
+ **Cache key** is used for storing the query state and for performing a fetch when it changes. Queries with the same cache key share their state.
277
+
278
+ Default implementation for `getCacheKey` is:
279
+ ```typescript
280
+ export const defaultGetCacheKey = <P = unknown>(params: P): Key => {
281
+ switch (typeof params) {
282
+ case 'string':
283
+ case 'symbol':
284
+ return params
285
+ case 'object':
286
+ return JSON.stringify(params)
287
+ default:
288
+ return String(params)
289
+ }
290
+ }
291
+ ```
292
+
293
+ It is recommended to override it when default implementation is not optimal or when keys in params object can be sorted in random order.
294
+
295
+ As example, can be overriden when implementing pagination.
@@ -13,18 +13,15 @@ const utilsAndConstants_1 = require("./utilsAndConstants");
13
13
  * Creates reducer, actions and hooks for managing queries and mutations through redux cache.
14
14
  */
15
15
  const createCache = (cache) => {
16
- var _a, _b, _c, _d, _e, _f;
17
- var _g, _h, _j;
18
- // @ts-expect-error hot
19
- const hotReloadEnabled = Boolean(module === null || module === void 0 ? void 0 : module.hot);
16
+ var _a, _b, _c, _d, _e;
17
+ var _f, _g;
20
18
  const abortControllers = new WeakMap();
21
19
  // provide all optional fields
22
20
  (_a = cache.options) !== null && _a !== void 0 ? _a : (cache.options = {});
23
- (_b = (_g = cache.options).logsEnabled) !== null && _b !== void 0 ? _b : (_g.logsEnabled = false);
24
- (_c = (_h = cache.options).validateFunctionArguments) !== null && _c !== void 0 ? _c : (_h.validateFunctionArguments = utilsAndConstants_1.isDev);
25
- (_d = (_j = cache.options).validateHookArguments) !== null && _d !== void 0 ? _d : (_j.validateHookArguments = utilsAndConstants_1.isDev && !hotReloadEnabled);
26
- (_e = cache.queries) !== null && _e !== void 0 ? _e : (cache.queries = {});
27
- (_f = cache.mutations) !== null && _f !== void 0 ? _f : (cache.mutations = {});
21
+ (_b = (_f = cache.options).logsEnabled) !== null && _b !== void 0 ? _b : (_f.logsEnabled = false);
22
+ (_c = (_g = cache.options).validateFunctionArguments) !== null && _c !== void 0 ? _c : (_g.validateFunctionArguments = utilsAndConstants_1.isDev);
23
+ (_d = cache.queries) !== null && _d !== void 0 ? _d : (cache.queries = {});
24
+ (_e = cache.mutations) !== null && _e !== void 0 ? _e : (cache.mutations = {});
28
25
  // @ts-expect-error for testing
29
26
  cache.abortControllers = abortControllers;
30
27
  const nonPartialCache = cache;
@@ -61,10 +58,9 @@ const createCache = (cache) => {
61
58
  return (0, react_1.useMemo)(() => {
62
59
  const client = {
63
60
  query: (options) => {
64
- var _a, _b;
61
+ var _a;
65
62
  const { query: queryKey, params } = options;
66
- const getParamsKey = (_a = nonPartialCache.queries[queryKey].getParamsKey) !== null && _a !== void 0 ? _a : (utilsAndConstants_1.defaultGetParamsKey);
67
- const getCacheKey = (_b = nonPartialCache.queries[queryKey].getCacheKey) !== null && _b !== void 0 ? _b : getParamsKey;
63
+ const getCacheKey = (_a = nonPartialCache.queries[queryKey].getCacheKey) !== null && _a !== void 0 ? _a : (utilsAndConstants_1.defaultGetCacheKey);
68
64
  // @ts-expect-error fix later
69
65
  const cacheKey = getCacheKey(params);
70
66
  return (0, query_1.query)('query', true, store, nonPartialCache, queryKey, cacheKey, params);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createCache } from './createCache';
2
2
  export type { ReduxCacheState } from './reducer';
3
3
  export * from './types';
4
- export { defaultGetParamsKey, defaultQueryMutationState } from './utilsAndConstants';
4
+ export { defaultGetCacheKey, defaultQueryMutationState } from './utilsAndConstants';
package/dist/index.js CHANGED
@@ -14,12 +14,12 @@ 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.defaultQueryMutationState = exports.defaultGetParamsKey = exports.createCache = void 0;
17
+ exports.defaultQueryMutationState = exports.defaultGetCacheKey = exports.createCache = void 0;
18
18
  var createCache_1 = require("./createCache");
19
19
  Object.defineProperty(exports, "createCache", { enumerable: true, get: function () { return createCache_1.createCache; } });
20
20
  __exportStar(require("./types"), exports);
21
21
  var utilsAndConstants_1 = require("./utilsAndConstants");
22
- Object.defineProperty(exports, "defaultGetParamsKey", { enumerable: true, get: function () { return utilsAndConstants_1.defaultGetParamsKey; } });
22
+ Object.defineProperty(exports, "defaultGetCacheKey", { enumerable: true, get: function () { return utilsAndConstants_1.defaultGetCacheKey; } });
23
23
  Object.defineProperty(exports, "defaultQueryMutationState", { enumerable: true, get: function () { return utilsAndConstants_1.defaultQueryMutationState; } });
24
24
  // Backlog
25
25
  // ! high
package/dist/types.d.ts CHANGED
@@ -35,12 +35,6 @@ export type CacheOptions = {
35
35
  * Default is true in dev mode.
36
36
  * */
37
37
  validateFunctionArguments: boolean;
38
- /**
39
- * Enables validation of package hook arguments. Recommened to enable in dev/testing mode and disable in production.
40
- * Should be disabled with hot reloading.
41
- * Default is true in dev mode without hot reloading.
42
- * */
43
- validateHookArguments: boolean;
44
38
  /**
45
39
  * Enable console logs.
46
40
  * Default is false.
@@ -73,15 +67,9 @@ export type QueryInfo<T extends Typenames, P, R, S> = {
73
67
  /** Merges results before saving to the store. */
74
68
  mergeResults?: (oldResult: R | undefined, response: QueryResponse<T, R>, params: P | undefined) => R;
75
69
  /**
76
- * Params key is used for determining if parameters were changed and fetch is needed.
77
- * Also used as cache key, of `getCacheKey` wasn't provided.
70
+ * Cache key is used for storing the query state and for performing a fetch when it changes. Queries with the same cache key share their state.
78
71
  * Default implementation uses `JSON.stringify` or `String()` depending on type.
79
- * */
80
- getParamsKey?: (params?: P) => Key;
81
- /**
82
- * Cache key is a key in redux state for caching query state.
83
- * Queries with equal cache keys have the same state.
84
- * Default implementation is equal to `getParamsKey`.
72
+ * It is recommended to override it when default implementation is not optimal or when keys in params object can be sorted in random order etc.
85
73
  * */
86
74
  getCacheKey?: (params?: P) => Key;
87
75
  };
@@ -18,21 +18,6 @@ const utilsAndConstants_1 = require("./utilsAndConstants");
18
18
  const useMutation = (cache, options, abortControllers) => {
19
19
  var _a;
20
20
  const { mutation: mutationKey } = options;
21
- // Check values that should be set once.
22
- // Can be removed from deps.
23
- cache.options.validateHookArguments &&
24
- (() => {
25
- ;
26
- [
27
- ['cache', cache],
28
- ['cache.options', cache.options],
29
- ['cache.options.logsEnabled', cache.options.logsEnabled],
30
- ['cacheStateSelector', cache.cacheStateSelector],
31
- ['mutationKey', mutationKey],
32
- ]
33
- // eslint-disable-next-line react-hooks/rules-of-hooks
34
- .forEach((args) => (0, utilsAndConstants_1.useAssertValueNotChanged)(...args));
35
- })();
36
21
  const store = (0, react_redux_1.useStore)();
37
22
  // Using single useMemo for performance reasons
38
23
  const [mutationStateSelector, mutate, abort] = (0, react_1.useMemo)(() => {
@@ -64,9 +49,8 @@ const useMutation = (cache, options, abortControllers) => {
64
49
  return true;
65
50
  },
66
51
  ];
67
- },
68
- // eslint-disable-next-line react-hooks/exhaustive-deps
69
- [store]);
52
+ // eslint-disable-next-line react-hooks/exhaustive-deps
53
+ }, [mutationKey, store]);
70
54
  // @ts-expect-error fix later
71
55
  const mutationState = (_a = (0, react_redux_1.useSelector)(mutationStateSelector)) !== null && _a !== void 0 ? _a : utilsAndConstants_1.defaultQueryMutationState;
72
56
  cache.options.logsEnabled &&
package/dist/useQuery.js CHANGED
@@ -16,56 +16,27 @@ const query_1 = require("./query");
16
16
  const utilsAndConstants_1 = require("./utilsAndConstants");
17
17
  const useQuery = (cache, options) => {
18
18
  var _a, _b, _c;
19
- const { query: queryKey, skip, params, cachePolicy = (_a = cache.queries[queryKey].cachePolicy) !== null && _a !== void 0 ? _a : 'cache-first', getCacheKey = cache.queries[queryKey].getCacheKey, } = options;
19
+ const { query: queryKey, skip, params, cachePolicy = (_a = cache.queries[queryKey].cachePolicy) !== null && _a !== void 0 ? _a : 'cache-first', getCacheKey = (_b = cache.queries[queryKey].getCacheKey) !== null && _b !== void 0 ? _b : (utilsAndConstants_1.defaultGetCacheKey), } = options;
20
20
  const logsEnabled = cache.options.logsEnabled;
21
- const getParamsKey = (_b = cache.queries[queryKey].getParamsKey) !== null && _b !== void 0 ? _b : (utilsAndConstants_1.defaultGetParamsKey);
22
21
  const cacheResultSelector = cache.queries[queryKey].resultSelector;
23
22
  const cacheStateSelector = cache.cacheStateSelector;
24
23
  const store = (0, react_redux_1.useStore)();
25
- // Check values that should be set once.
26
- cache.options.validateHookArguments &&
27
- (() => {
28
- ;
29
- [
30
- ['store', store],
31
- ['cache', cache],
32
- ['cache.queries', cache.queries],
33
- ['cacheStateSelector', cache.cacheStateSelector],
34
- ['options.query', options.query],
35
- ['queryKey', queryKey],
36
- ['resultSelector', cache.queries[queryKey].resultSelector],
37
- ['mergeResults', cache.queries[queryKey].mergeResults],
38
- ['getParamsKey', cache.queries[queryKey].getParamsKey],
39
- ['getCacheKey', cache.queries[queryKey].getCacheKey],
40
- ]
41
- // eslint-disable-next-line react-hooks/rules-of-hooks
42
- .forEach((args) => (0, utilsAndConstants_1.useAssertValueNotChanged)(...args));
43
- })();
44
- const paramsKey = getParamsKey(
45
- // @ts-expect-error fix later
46
- params);
47
- const [cacheKey, resultSelector] = (0, react_1.useMemo)(() => {
48
- return [
49
- // cacheKey
50
- getCacheKey
51
- ? // @ts-expect-error fix types later
52
- getCacheKey(params)
53
- : paramsKey,
54
- // resultSelector
55
- cacheResultSelector &&
56
- ((state) => cacheResultSelector(cacheStateSelector(state),
57
- // @ts-expect-error fix types later
58
- params)),
59
- ];
24
+ // @ts-expect-error fix types later
25
+ const cacheKey = getCacheKey(params);
26
+ const resultSelector = (0, react_1.useMemo)(() => {
27
+ return (cacheResultSelector &&
28
+ ((state) => cacheResultSelector(cacheStateSelector(state),
29
+ // @ts-expect-error fix types later
30
+ params)));
60
31
  // eslint-disable-next-line react-hooks/exhaustive-deps
61
- }, [paramsKey]);
32
+ }, [cacheKey]);
62
33
  // eslint-disable-next-line react-hooks/rules-of-hooks
63
34
  const resultFromSelector = (resultSelector && (0, react_redux_1.useSelector)(resultSelector));
64
35
  const hasResultFromSelector = resultFromSelector !== undefined;
65
36
  const fetch = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
66
37
  yield (0, query_1.query)('useQuery.fetch', false, store, cache, queryKey, cacheKey, params);
67
38
  // eslint-disable-next-line react-hooks/exhaustive-deps
68
- }), [cacheKey, paramsKey]);
39
+ }), [store, queryKey, cacheKey]);
69
40
  const queryStateFromSelector = (_c = (0, react_redux_1.useSelector)((state) => {
70
41
  const queryState = cacheStateSelector(state).queries[queryKey][cacheKey];
71
42
  return queryState; // TODO proper type
@@ -75,7 +46,7 @@ const useQuery = (cache, options) => {
75
46
  : queryStateFromSelector;
76
47
  (0, react_1.useEffect)(() => {
77
48
  if (skip) {
78
- logsEnabled && (0, utilsAndConstants_1.log)('useQuery.useEffect skip fetch', { skip, paramsKey });
49
+ logsEnabled && (0, utilsAndConstants_1.log)('useQuery.useEffect skip fetch', { skip, cacheKey });
79
50
  return;
80
51
  }
81
52
  if (queryState.result != null && cachePolicy === 'cache-first') {
@@ -88,10 +59,9 @@ const useQuery = (cache, options) => {
88
59
  }
89
60
  fetch();
90
61
  // eslint-disable-next-line react-hooks/exhaustive-deps
91
- }, [paramsKey, cachePolicy, skip]);
62
+ }, [cacheKey, cachePolicy, skip]);
92
63
  logsEnabled &&
93
64
  (0, utilsAndConstants_1.log)('useQuery', {
94
- paramsKey,
95
65
  cacheKey,
96
66
  options,
97
67
  resultFromSelector,
@@ -1,12 +1,11 @@
1
- import { CacheOptions, EntitiesMap, EntityChanges, Typenames } from './types';
1
+ import { CacheOptions, EntitiesMap, EntityChanges, Key, Typenames } from './types';
2
2
  export declare const PACKAGE_SHORT_NAME = "RRC";
3
3
  export declare const isDev: boolean;
4
4
  export declare const defaultQueryMutationState: {
5
5
  readonly loading: false;
6
6
  readonly error: undefined;
7
7
  };
8
- export declare const defaultGetParamsKey: <P = unknown>(params: P) => string;
9
- export declare const useAssertValueNotChanged: (name: string, value: unknown) => void;
8
+ export declare const defaultGetCacheKey: <P = unknown>(params: P) => Key;
10
9
  export declare const log: (tag: string, data?: unknown) => void;
11
10
  /**
12
11
  * Apply changes to the entities map.
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.applyEntityChanges = exports.log = exports.useAssertValueNotChanged = exports.defaultGetParamsKey = exports.defaultQueryMutationState = exports.isDev = exports.PACKAGE_SHORT_NAME = void 0;
4
- const react_1 = require("react");
3
+ exports.applyEntityChanges = exports.log = exports.defaultGetCacheKey = exports.defaultQueryMutationState = exports.isDev = exports.PACKAGE_SHORT_NAME = void 0;
5
4
  exports.PACKAGE_SHORT_NAME = 'RRC';
6
5
  exports.isDev = (() => {
7
6
  try {
@@ -13,9 +12,10 @@ exports.isDev = (() => {
13
12
  }
14
13
  })();
15
14
  exports.defaultQueryMutationState = { loading: false, error: undefined };
16
- const defaultGetParamsKey = (params) => {
15
+ const defaultGetCacheKey = (params) => {
17
16
  switch (typeof params) {
18
17
  case 'string':
18
+ case 'symbol':
19
19
  return params;
20
20
  case 'object':
21
21
  return JSON.stringify(params);
@@ -23,18 +23,7 @@ const defaultGetParamsKey = (params) => {
23
23
  return String(params);
24
24
  }
25
25
  };
26
- exports.defaultGetParamsKey = defaultGetParamsKey;
27
- const useAssertValueNotChanged = (name, value) => {
28
- const firstMountRef = (0, react_1.useRef)(false);
29
- (0, react_1.useMemo)(() => {
30
- if (firstMountRef.current) {
31
- throw new Error(`${name} should not be modified`);
32
- }
33
- firstMountRef.current = true;
34
- // eslint-disable-next-line react-hooks/exhaustive-deps
35
- }, [value]);
36
- };
37
- exports.useAssertValueNotChanged = useAssertValueNotChanged;
26
+ exports.defaultGetCacheKey = defaultGetCacheKey;
38
27
  const log = (tag, data) => {
39
28
  console.debug(`@${exports.PACKAGE_SHORT_NAME} [${tag}]`, data);
40
29
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "react-redux-cache",
3
3
  "author": "Alexander Danilov",
4
4
  "license": "MIT",
5
- "version": "0.0.11",
5
+ "version": "0.0.12",
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",