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 +68 -0
- package/dist/createCache.js +8 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/types.d.ts +2 -14
- package/dist/useMutation.js +2 -18
- package/dist/useQuery.js +12 -42
- package/dist/utilsAndConstants.d.ts +2 -3
- package/dist/utilsAndConstants.js +4 -15
- package/package.json +1 -1
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.
|
package/dist/createCache.js
CHANGED
|
@@ -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
|
|
17
|
-
var
|
|
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 = (
|
|
24
|
-
(_c = (
|
|
25
|
-
(_d =
|
|
26
|
-
(_e = cache.
|
|
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
|
|
61
|
+
var _a;
|
|
65
62
|
const { query: queryKey, params } = options;
|
|
66
|
-
const
|
|
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 {
|
|
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.
|
|
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, "
|
|
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
|
-
*
|
|
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
|
};
|
package/dist/useMutation.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
}, [
|
|
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
|
-
}), [
|
|
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,
|
|
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
|
-
}, [
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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",
|