react-redux-cache 0.6.0 → 0.7.0-rc.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/README.md +66 -30
- package/dist/createActions.d.ts +37 -11
- package/dist/createActions.js +10 -2
- package/dist/createCache.d.ts +49 -27
- package/dist/createCache.js +11 -5
- package/dist/{reducer.d.ts → createCacheReducer.d.ts} +15 -8
- package/dist/{reducer.js → createCacheReducer.js} +74 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -9
- package/dist/mutate.js +4 -4
- package/dist/query.d.ts +1 -1
- package/dist/query.js +16 -6
- package/dist/types.d.ts +27 -10
- package/dist/useMutation.d.ts +2 -2
- package/dist/useQuery.d.ts +2 -4
- package/dist/useQuery.js +26 -5
- package/dist/utilsAndConstants.d.ts +3 -3
- package/dist/utilsAndConstants.js +3 -5
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Examples of states, generated by cache reducer from `/example` project:
|
|
|
41
41
|
queries: {
|
|
42
42
|
// each query has its own map of query states, stored by cache key, which is generated from query params
|
|
43
43
|
getUser: {
|
|
44
|
-
"2": {loading: false,
|
|
44
|
+
"2": {loading: false, result: 2, params: 2, expiresAt: 1727217298025},
|
|
45
45
|
"3": {loading: true, params: 3}
|
|
46
46
|
},
|
|
47
47
|
getUsers: {
|
|
@@ -79,9 +79,9 @@ Examples of states, generated by cache reducer from `/example` project:
|
|
|
79
79
|
getUser: {
|
|
80
80
|
"2": {
|
|
81
81
|
loading: false,
|
|
82
|
-
error: undefined,
|
|
83
82
|
result: {id: 2, bank: {id: "2", name: "Bank 2"}, name: "User 2"},
|
|
84
|
-
params: 2
|
|
83
|
+
params: 2,
|
|
84
|
+
expiresAt: 1727217298025
|
|
85
85
|
},
|
|
86
86
|
"3": {loading: true, params: 3}
|
|
87
87
|
},
|
|
@@ -122,6 +122,8 @@ Examples of states, generated by cache reducer from `/example` project:
|
|
|
122
122
|
- [api.ts](https://github.com/gentlee/react-redux-cache#apits)
|
|
123
123
|
- [Usage](https://github.com/gentlee/react-redux-cache#usage)
|
|
124
124
|
- [Advanced](https://github.com/gentlee/react-redux-cache#advanced)
|
|
125
|
+
- [Error handling](https://github.com/gentlee/react-redux-cache#error-handling)
|
|
126
|
+
- [Invalidation](https://github.com/gentlee/react-redux-cache#invalidation)
|
|
125
127
|
- [Extended cache policy](https://github.com/gentlee/react-redux-cache#extended-cache-policy)
|
|
126
128
|
- [Infinite scroll pagination](https://github.com/gentlee/react-redux-cache#infinite-scroll-pagination)
|
|
127
129
|
- [redux-persist](https://github.com/gentlee/react-redux-cache#redux-persist)
|
|
@@ -134,7 +136,7 @@ Examples of states, generated by cache reducer from `/example` project:
|
|
|
134
136
|
|
|
135
137
|
`fast-deep-equal` is an optional peer dependency if `deepComparisonEnabled` cache option is enabled (default is true).
|
|
136
138
|
```sh
|
|
137
|
-
npm add react-redux-cache react redux react-redux
|
|
139
|
+
npm add react-redux-cache react redux react-redux fast-deep-equal
|
|
138
140
|
```
|
|
139
141
|
### Initialization
|
|
140
142
|
The only function that needs to be imported is `createCache`, which creates fully typed reducer, hooks, actions, selectors and utils to be used in the app. You can create as many caches as needed, but keep in mind that normalization is not shared between them.
|
|
@@ -156,7 +158,12 @@ export const {
|
|
|
156
158
|
},
|
|
157
159
|
queries: {
|
|
158
160
|
getUsers: { query: getUsers },
|
|
159
|
-
getUser: {
|
|
161
|
+
getUser: {
|
|
162
|
+
query: getUser,
|
|
163
|
+
// For each query `secondsToLive` option can be set, which is used to set expiration date of a cached result when query response is received.
|
|
164
|
+
// After expiration query result is considered invalidated and will be refetched on the next useQuery mount.
|
|
165
|
+
secondsToLive: 5 * 60 // Here cached result is valid for 5 minutes.
|
|
166
|
+
},
|
|
160
167
|
},
|
|
161
168
|
mutations: {
|
|
162
169
|
updateUser: { mutation: updateUser },
|
|
@@ -201,37 +208,32 @@ Perfect implementation is when the backend already returns normalized data.
|
|
|
201
208
|
|
|
202
209
|
// Example of query with normalization (recommended)
|
|
203
210
|
|
|
204
|
-
export const getUser = async (id
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
// result is id of the user
|
|
209
|
-
result: number
|
|
210
|
-
// entities contain all normalized objects
|
|
211
|
-
entities: {
|
|
212
|
-
users: Record<number, User>
|
|
213
|
-
banks: Record<string, Bank>
|
|
214
|
-
}
|
|
215
|
-
} = normalize(result, getUserSchema)
|
|
211
|
+
export const getUser = async (id) => {
|
|
212
|
+
// Result can be get by any way - fetch, axios etc, even with database connection.
|
|
213
|
+
// There is no limitation here.
|
|
214
|
+
const response = await ...
|
|
216
215
|
|
|
217
|
-
|
|
218
|
-
|
|
216
|
+
// In this example normalizr package is used, but it is not necessary.
|
|
217
|
+
return normalize(response, getUserSchema)
|
|
218
|
+
// satisfies keyword is used here for proper typing of params and returned value.
|
|
219
|
+
} satisfies Query<number, CacheTypenames>
|
|
219
220
|
|
|
220
|
-
// Example of query without normalization (not recommended)
|
|
221
|
+
// Example of query without normalization (not recommended), with selecting access token from the store
|
|
221
222
|
|
|
222
|
-
export const getBank = (id
|
|
223
|
+
export const getBank = (id, {getState}) => {
|
|
224
|
+
const token = tokenSelector(getState())
|
|
223
225
|
const result: Bank = ...
|
|
224
226
|
return {result} // result is bank object, no entities passed
|
|
225
|
-
}
|
|
227
|
+
} satisfies Query<string>
|
|
226
228
|
|
|
227
229
|
// Example of mutation with normalization
|
|
228
230
|
|
|
229
|
-
export const removeUser = async (id
|
|
231
|
+
export const removeUser = async (id, _, abortSignal) => {
|
|
230
232
|
await ...
|
|
231
233
|
return {
|
|
232
234
|
remove: { users: [id] },
|
|
233
235
|
}
|
|
234
|
-
}
|
|
236
|
+
} satisfies Query<number, CacheTypenames>
|
|
235
237
|
```
|
|
236
238
|
|
|
237
239
|
### Usage
|
|
@@ -243,7 +245,7 @@ Please check `example/` folder (`npm run example` to run).
|
|
|
243
245
|
export const UserScreen = () => {
|
|
244
246
|
const {id} = useParams()
|
|
245
247
|
|
|
246
|
-
// useQuery connects to redux state and if user with that id is already cached, fetch won't happen (with default cachePolicy 'cache-first')
|
|
248
|
+
// useQuery connects to redux state and if user with that id is already cached, fetch won't happen (with default cachePolicy 'cache-first').
|
|
247
249
|
// Infers all types from created cache, telling here that params and result are of type `number`.
|
|
248
250
|
const [{result: userId, loading, error}] = useQuery({
|
|
249
251
|
query: 'getUser',
|
|
@@ -254,7 +256,7 @@ export const UserScreen = () => {
|
|
|
254
256
|
mutation: 'updateUser',
|
|
255
257
|
})
|
|
256
258
|
|
|
257
|
-
// This selector
|
|
259
|
+
// This selector returns entities with proper types - User and Bank
|
|
258
260
|
const user = useSelectEntityById(userId, 'users')
|
|
259
261
|
const bank = useSelectEntityById(user?.bankId, 'banks')
|
|
260
262
|
|
|
@@ -268,9 +270,43 @@ export const UserScreen = () => {
|
|
|
268
270
|
|
|
269
271
|
### Advanced
|
|
270
272
|
|
|
273
|
+
#### Error handling
|
|
274
|
+
|
|
275
|
+
Queries and mutations are wrapped in try/catch, so any error will lead to cancelling of any updates to the state except `loading: false` and the caught error. If you still want to make some state updates, or just want to use thrown errors only for unexpected cases, consider returning expected errors as a part of the result:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
export const updateBank = (bank) => {
|
|
279
|
+
const {httpError, response} = ...
|
|
280
|
+
return {
|
|
281
|
+
result: {
|
|
282
|
+
// Error is a part of the result, containing e.g. map of not valid fields and threir error messages
|
|
283
|
+
httpError,
|
|
284
|
+
// Bank still can be returned from the backend with error e.g. when only some of fields were udpated
|
|
285
|
+
bank: response?.bank
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} satisfies Mutation<Partial<Bank>>
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### Invalidation
|
|
293
|
+
|
|
294
|
+
`cache-first` cache policy (default) skips fetching on component mount if result is already cached, but we can invalidate cached query results using `invalidateQuery` action to make it run again on a next mount. Also, we can return this action as a part of query or mutation response, to perform it in one batch update.
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const updateUser = (user: Partial<User>) => {
|
|
298
|
+
const response = ...
|
|
299
|
+
return {
|
|
300
|
+
result: response.result,
|
|
301
|
+
expiresAt: Date.now() + 10000, // optional, expiration time of current response
|
|
302
|
+
actions: [invalidateQuery([{ key: 'getUsers' }])] // optional, this action sets `expiresAt` field of `getUsers` states to Date.now()
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
271
307
|
#### Extended cache policy
|
|
272
308
|
|
|
273
|
-
`cache-first` cache policy skips fetching if result is already cached, but sometimes it can't determine that we already have result in some other's query result or in normalized entities cache. In that case we can use `skip` parameter of a query:
|
|
309
|
+
`cache-first` cache policy (default) skips fetching on component mount if result is already cached, but sometimes it can't determine that we already have result in some other's query result or in normalized entities cache. In that case we can use `skip` parameter of a query:
|
|
274
310
|
|
|
275
311
|
```typescript
|
|
276
312
|
export const UserScreen = () => {
|
|
@@ -294,7 +330,7 @@ We can additionally check that entity is full or "fresh" enough:
|
|
|
294
330
|
skip: !!user && isFullUser(user)
|
|
295
331
|
```
|
|
296
332
|
|
|
297
|
-
Another approach is to set `skip: true` and manually run `fetch`
|
|
333
|
+
Another approach is to set `skip: true` and manually run `fetch`. `onlyIfExpired` option can be also used:
|
|
298
334
|
|
|
299
335
|
```typescript
|
|
300
336
|
export const UserScreen = () => {
|
|
@@ -308,7 +344,7 @@ export const UserScreen = () => {
|
|
|
308
344
|
|
|
309
345
|
useEffect(() => {
|
|
310
346
|
if (screenIsVisible) {
|
|
311
|
-
fetchUser()
|
|
347
|
+
fetchUser({ onlyIfExpired: true }) // expiration happens if expiresAt was set before e.g. by secondsToLive option or invalidateQuery action. If result is not cached yet, it is also considered as expired.
|
|
312
348
|
}
|
|
313
349
|
}, [screenIsVisible])
|
|
314
350
|
|
|
@@ -439,4 +475,4 @@ As example, can be overriden when implementing pagination.
|
|
|
439
475
|
|
|
440
476
|
**Queries:** Queries are throttled: query with the same cache key (generated from params by default) is cancelled if already running.
|
|
441
477
|
|
|
442
|
-
**Mutations:** Mutations are debounced: previous similar mutation is aborted if it was running when the new one started.
|
|
478
|
+
**Mutations:** Mutations are debounced: previous similar mutation is aborted if it was running when the new one started. Third argument in mutations is `AbortSignal`, which can be used e.g. for cancelling http requests.
|
package/dist/createActions.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import type { EntityChanges, Key,
|
|
1
|
+
import type { EntityChanges, Key, MutationState, QueryState, Typenames } from './types';
|
|
2
2
|
export type ActionMap<N extends string, T extends Typenames, QP, QR, MP, MR> = ReturnType<typeof createActions<N, T, QP, QR, MP, MR>>;
|
|
3
3
|
export declare const createActions: <N extends string, T extends Typenames, QP, QR, MP, MR>(name: N) => {
|
|
4
4
|
/** Updates query state, and optionally merges entity changes in a single action. */
|
|
5
5
|
updateQueryStateAndEntities: {
|
|
6
|
-
<K extends keyof QP & keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<
|
|
6
|
+
<K extends keyof QP & keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<QueryState<QP[K], QR[K]>> | undefined, entityChanges?: EntityChanges<T> | undefined): {
|
|
7
7
|
type: `@rrc/${N}/updateQueryStateAndEntities`;
|
|
8
8
|
queryKey: K;
|
|
9
9
|
queryCacheKey: Key;
|
|
10
|
-
state: Partial<
|
|
10
|
+
state: Partial<QueryState<QP[K], QR[K]>> | undefined;
|
|
11
11
|
entityChanges: EntityChanges<T> | undefined;
|
|
12
12
|
};
|
|
13
13
|
type: `@rrc/${N}/updateQueryStateAndEntities`;
|
|
14
14
|
};
|
|
15
15
|
/** Updates mutation state, and optionally merges entity changes in a single action. */
|
|
16
16
|
updateMutationStateAndEntities: {
|
|
17
|
-
<K_1 extends keyof MP & keyof MR>(mutationKey: K_1, state?: Partial<
|
|
17
|
+
<K_1 extends keyof MP & keyof MR>(mutationKey: K_1, state?: Partial<MutationState<MP[K_1], MR[K_1]>> | undefined, entityChanges?: EntityChanges<T> | undefined): {
|
|
18
18
|
type: `@rrc/${N}/updateMutationStateAndEntities`;
|
|
19
19
|
mutationKey: K_1;
|
|
20
|
-
state: Partial<
|
|
20
|
+
state: Partial<MutationState<MP[K_1], MR[K_1]>> | undefined;
|
|
21
21
|
entityChanges: EntityChanges<T> | undefined;
|
|
22
22
|
};
|
|
23
23
|
type: `@rrc/${N}/updateMutationStateAndEntities`;
|
|
@@ -30,16 +30,42 @@ export declare const createActions: <N extends string, T extends Typenames, QP,
|
|
|
30
30
|
};
|
|
31
31
|
type: `@rrc/${N}/mergeEntityChanges`;
|
|
32
32
|
};
|
|
33
|
+
/** Invalidates query states. */
|
|
34
|
+
invalidateQuery: {
|
|
35
|
+
<K_2 extends keyof QP & keyof QR>(queries: {
|
|
36
|
+
/** Query key */
|
|
37
|
+
key: K_2;
|
|
38
|
+
/** Query cache key */
|
|
39
|
+
cacheKey?: Key | undefined;
|
|
40
|
+
/** Unix timestamp at which query expires. Is set to the query state. @default Date.now() */
|
|
41
|
+
expiresAt?: number | undefined;
|
|
42
|
+
}[]): {
|
|
43
|
+
type: `@rrc/${N}/invalidateQuery`;
|
|
44
|
+
queries: {
|
|
45
|
+
/** Query key */
|
|
46
|
+
key: K_2;
|
|
47
|
+
/** Query cache key */
|
|
48
|
+
cacheKey?: Key | undefined;
|
|
49
|
+
/** Unix timestamp at which query expires. Is set to the query state. @default Date.now() */
|
|
50
|
+
expiresAt?: number | undefined;
|
|
51
|
+
}[];
|
|
52
|
+
};
|
|
53
|
+
type: `@rrc/${N}/invalidateQuery`;
|
|
54
|
+
};
|
|
33
55
|
/** Clear states for provided query keys and cache keys.
|
|
34
56
|
* If cache key for query key is not provided, the whole state for query key is cleared. */
|
|
35
57
|
clearQueryState: {
|
|
36
|
-
<
|
|
37
|
-
key
|
|
58
|
+
<K_3 extends keyof QP & keyof QR>(queries: {
|
|
59
|
+
/** Query key */
|
|
60
|
+
key: K_3;
|
|
61
|
+
/** Query cache key */
|
|
38
62
|
cacheKey?: Key | undefined;
|
|
39
63
|
}[]): {
|
|
40
64
|
type: `@rrc/${N}/clearQueryState`;
|
|
41
|
-
|
|
42
|
-
key
|
|
65
|
+
queries: {
|
|
66
|
+
/** Query key */
|
|
67
|
+
key: K_3;
|
|
68
|
+
/** Query cache key */
|
|
43
69
|
cacheKey?: Key | undefined;
|
|
44
70
|
}[];
|
|
45
71
|
};
|
|
@@ -47,9 +73,9 @@ export declare const createActions: <N extends string, T extends Typenames, QP,
|
|
|
47
73
|
};
|
|
48
74
|
/** Clear states for provided mutation keys. */
|
|
49
75
|
clearMutationState: {
|
|
50
|
-
<
|
|
76
|
+
<K_4 extends keyof MP & keyof MR>(mutationKeys: K_4[]): {
|
|
51
77
|
type: `@rrc/${N}/clearMutationState`;
|
|
52
|
-
mutationKeys:
|
|
78
|
+
mutationKeys: K_4[];
|
|
53
79
|
};
|
|
54
80
|
type: `@rrc/${N}/clearMutationState`;
|
|
55
81
|
};
|
package/dist/createActions.js
CHANGED
|
@@ -27,10 +27,16 @@ const createActions = (name) => {
|
|
|
27
27
|
changes,
|
|
28
28
|
});
|
|
29
29
|
mergeEntityChanges.type = mergeEntityChangesType;
|
|
30
|
+
const invalidateQueryType = `${actionPrefix}invalidateQuery`;
|
|
31
|
+
const invalidateQuery = (queries) => ({
|
|
32
|
+
type: invalidateQueryType,
|
|
33
|
+
queries,
|
|
34
|
+
});
|
|
35
|
+
invalidateQuery.type = invalidateQueryType;
|
|
30
36
|
const clearQueryStateType = `${actionPrefix}clearQueryState`;
|
|
31
|
-
const clearQueryState = (
|
|
37
|
+
const clearQueryState = (queries) => ({
|
|
32
38
|
type: clearQueryStateType,
|
|
33
|
-
|
|
39
|
+
queries,
|
|
34
40
|
});
|
|
35
41
|
clearQueryState.type = clearQueryStateType;
|
|
36
42
|
const clearMutationStateType = `${actionPrefix}clearMutationState`;
|
|
@@ -46,6 +52,8 @@ const createActions = (name) => {
|
|
|
46
52
|
updateMutationStateAndEntities,
|
|
47
53
|
/** Merge EntityChanges to the state. */
|
|
48
54
|
mergeEntityChanges,
|
|
55
|
+
/** Invalidates query states. */
|
|
56
|
+
invalidateQuery,
|
|
49
57
|
/** Clear states for provided query keys and cache keys.
|
|
50
58
|
* If cache key for query key is not provided, the whole state for query key is cleared. */
|
|
51
59
|
clearQueryState,
|
package/dist/createCache.d.ts
CHANGED
|
@@ -1,35 +1,42 @@
|
|
|
1
|
-
import type { Cache, Key, MutationResult,
|
|
1
|
+
import type { Cache, Key, MutationResult, MutationState, OptionalPartial, QueryOptions, QueryResult, QueryState, Typenames } from './types';
|
|
2
2
|
import { useMutation } from './useMutation';
|
|
3
3
|
import { useQuery } from './useQuery';
|
|
4
4
|
import { applyEntityChanges } from './utilsAndConstants';
|
|
5
5
|
/**
|
|
6
6
|
* Creates reducer, actions and hooks for managing queries and mutations through redux cache.
|
|
7
7
|
*/
|
|
8
|
-
export declare const createCache: <N extends string, T extends Typenames, QP, QR, MP, MR>(partialCache: OptionalPartial<Cache<N, T, QP, QR, MP, MR>, "
|
|
8
|
+
export declare const createCache: <N extends string, T extends Typenames, QP, QR, MP, MR>(partialCache: OptionalPartial<Cache<N, T, QP, QR, MP, MR>, "queries" | "options" | "mutations" | "cacheStateSelector">) => {
|
|
9
9
|
/** Keeps all options, passed while creating the cache. */
|
|
10
10
|
cache: Cache<N, T, QP, QR, MP, MR>;
|
|
11
11
|
/** Reducer of the cache, should be added to redux store. */
|
|
12
12
|
reducer: (state: {
|
|
13
13
|
entities: import("./types").EntitiesMap<T>;
|
|
14
|
-
queries: QP | QR extends infer T_1 ? { [QK in keyof T_1]: import("./types").Dict<
|
|
15
|
-
mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]:
|
|
14
|
+
queries: QP | QR extends infer T_1 ? { [QK in keyof T_1]: import("./types").Dict<QueryState<QP[QK], QR[QK]> | undefined>; } : never;
|
|
15
|
+
mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]: MutationState<MP[MK], MR[MK]>; } : never;
|
|
16
16
|
} | undefined, action: {
|
|
17
17
|
type: `@rrc/${N}/updateQueryStateAndEntities`;
|
|
18
18
|
queryKey: keyof QP & keyof QR;
|
|
19
19
|
queryCacheKey: Key;
|
|
20
|
-
state: Partial<
|
|
20
|
+
state: Partial<QueryState<QP[keyof QP & keyof QR], QR[keyof QP & keyof QR]>> | undefined;
|
|
21
21
|
entityChanges: import("./types").EntityChanges<T> | undefined;
|
|
22
22
|
} | {
|
|
23
23
|
type: `@rrc/${N}/updateMutationStateAndEntities`;
|
|
24
24
|
mutationKey: keyof MP & keyof MR;
|
|
25
|
-
state: Partial<
|
|
25
|
+
state: Partial<MutationState<MP[keyof MP & keyof MR], MR[keyof MP & keyof MR]>> | undefined;
|
|
26
26
|
entityChanges: import("./types").EntityChanges<T> | undefined;
|
|
27
27
|
} | {
|
|
28
28
|
type: `@rrc/${N}/mergeEntityChanges`;
|
|
29
29
|
changes: import("./types").EntityChanges<T>;
|
|
30
|
+
} | {
|
|
31
|
+
type: `@rrc/${N}/invalidateQuery`;
|
|
32
|
+
queries: {
|
|
33
|
+
key: keyof QP & keyof QR;
|
|
34
|
+
cacheKey?: Key | undefined;
|
|
35
|
+
expiresAt?: number | undefined;
|
|
36
|
+
}[];
|
|
30
37
|
} | {
|
|
31
38
|
type: `@rrc/${N}/clearQueryState`;
|
|
32
|
-
|
|
39
|
+
queries: {
|
|
33
40
|
key: keyof QP & keyof QR;
|
|
34
41
|
cacheKey?: Key | undefined;
|
|
35
42
|
}[];
|
|
@@ -38,25 +45,25 @@ export declare const createCache: <N extends string, T extends Typenames, QP, QR
|
|
|
38
45
|
mutationKeys: (keyof MP & keyof MR)[];
|
|
39
46
|
}) => {
|
|
40
47
|
entities: import("./types").EntitiesMap<T>;
|
|
41
|
-
queries: QP | QR extends infer T_3 ? { [QK in keyof T_3]: import("./types").Dict<
|
|
42
|
-
mutations: MP | MR extends infer T_4 ? { [MK in keyof T_4]:
|
|
48
|
+
queries: QP | QR extends infer T_3 ? { [QK in keyof T_3]: import("./types").Dict<QueryState<QP[QK], QR[QK]> | undefined>; } : never;
|
|
49
|
+
mutations: MP | MR extends infer T_4 ? { [MK in keyof T_4]: MutationState<MP[MK], MR[MK]>; } : never;
|
|
43
50
|
};
|
|
44
51
|
actions: {
|
|
45
52
|
updateQueryStateAndEntities: {
|
|
46
|
-
<K extends keyof QP & keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<
|
|
53
|
+
<K extends keyof QP & keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<QueryState<QP[K], QR[K]>> | undefined, entityChanges?: import("./types").EntityChanges<T> | undefined): {
|
|
47
54
|
type: `@rrc/${N}/updateQueryStateAndEntities`;
|
|
48
55
|
queryKey: K;
|
|
49
56
|
queryCacheKey: Key;
|
|
50
|
-
state: Partial<
|
|
57
|
+
state: Partial<QueryState<QP[K], QR[K]>> | undefined;
|
|
51
58
|
entityChanges: import("./types").EntityChanges<T> | undefined;
|
|
52
59
|
};
|
|
53
60
|
type: `@rrc/${N}/updateQueryStateAndEntities`;
|
|
54
61
|
};
|
|
55
62
|
updateMutationStateAndEntities: {
|
|
56
|
-
<K_1 extends keyof MP & keyof MR>(mutationKey: K_1, state?: Partial<
|
|
63
|
+
<K_1 extends keyof MP & keyof MR>(mutationKey: K_1, state?: Partial<MutationState<MP[K_1], MR[K_1]>> | undefined, entityChanges?: import("./types").EntityChanges<T> | undefined): {
|
|
57
64
|
type: `@rrc/${N}/updateMutationStateAndEntities`;
|
|
58
65
|
mutationKey: K_1;
|
|
59
|
-
state: Partial<
|
|
66
|
+
state: Partial<MutationState<MP[K_1], MR[K_1]>> | undefined;
|
|
60
67
|
entityChanges: import("./types").EntityChanges<T> | undefined;
|
|
61
68
|
};
|
|
62
69
|
type: `@rrc/${N}/updateMutationStateAndEntities`;
|
|
@@ -68,30 +75,45 @@ export declare const createCache: <N extends string, T extends Typenames, QP, QR
|
|
|
68
75
|
};
|
|
69
76
|
type: `@rrc/${N}/mergeEntityChanges`;
|
|
70
77
|
};
|
|
71
|
-
|
|
72
|
-
<K_2 extends keyof QP & keyof QR>(
|
|
78
|
+
invalidateQuery: {
|
|
79
|
+
<K_2 extends keyof QP & keyof QR>(queries: {
|
|
73
80
|
key: K_2;
|
|
74
81
|
cacheKey?: Key | undefined;
|
|
82
|
+
expiresAt?: number | undefined;
|
|
75
83
|
}[]): {
|
|
76
|
-
type: `@rrc/${N}/
|
|
77
|
-
|
|
84
|
+
type: `@rrc/${N}/invalidateQuery`;
|
|
85
|
+
queries: {
|
|
78
86
|
key: K_2;
|
|
79
87
|
cacheKey?: Key | undefined;
|
|
88
|
+
expiresAt?: number | undefined;
|
|
89
|
+
}[];
|
|
90
|
+
};
|
|
91
|
+
type: `@rrc/${N}/invalidateQuery`;
|
|
92
|
+
};
|
|
93
|
+
clearQueryState: {
|
|
94
|
+
<K_3 extends keyof QP & keyof QR>(queries: {
|
|
95
|
+
key: K_3;
|
|
96
|
+
cacheKey?: Key | undefined;
|
|
97
|
+
}[]): {
|
|
98
|
+
type: `@rrc/${N}/clearQueryState`;
|
|
99
|
+
queries: {
|
|
100
|
+
key: K_3;
|
|
101
|
+
cacheKey?: Key | undefined;
|
|
80
102
|
}[];
|
|
81
103
|
};
|
|
82
104
|
type: `@rrc/${N}/clearQueryState`;
|
|
83
105
|
};
|
|
84
106
|
clearMutationState: {
|
|
85
|
-
<
|
|
107
|
+
<K_4 extends keyof MP & keyof MR>(mutationKeys: K_4[]): {
|
|
86
108
|
type: `@rrc/${N}/clearMutationState`;
|
|
87
|
-
mutationKeys:
|
|
109
|
+
mutationKeys: K_4[];
|
|
88
110
|
};
|
|
89
111
|
type: `@rrc/${N}/clearMutationState`;
|
|
90
112
|
};
|
|
91
113
|
};
|
|
92
114
|
selectors: {
|
|
93
115
|
/** Selects query state. */
|
|
94
|
-
selectQueryState: <QK_1 extends keyof QP | keyof QR>(state: unknown, query: QK_1, cacheKey: Key) =>
|
|
116
|
+
selectQueryState: <QK_1 extends keyof QP | keyof QR>(state: unknown, query: QK_1, cacheKey: Key) => QueryState<QK_1 extends keyof QP & keyof QR ? QP[QK_1] : never, QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never>;
|
|
95
117
|
/** Selects query latest result. */
|
|
96
118
|
selectQueryResult: <QK_2 extends keyof QP | keyof QR>(state: unknown, query: QK_2, cacheKey: Key) => (QK_2 extends keyof QP & keyof QR ? QR[QK_2] : never) | undefined;
|
|
97
119
|
/** Selects query loading state. */
|
|
@@ -100,8 +122,10 @@ export declare const createCache: <N extends string, T extends Typenames, QP, QR
|
|
|
100
122
|
selectQueryError: <QK_4 extends keyof QP | keyof QR>(state: unknown, query: QK_4, cacheKey: Key) => Error | undefined;
|
|
101
123
|
/** Selects query latest params. */
|
|
102
124
|
selectQueryParams: <QK_5 extends keyof QP | keyof QR>(state: unknown, query: QK_5, cacheKey: Key) => (QK_5 extends keyof QP & keyof QR ? QP[QK_5] : never) | undefined;
|
|
125
|
+
/** Selects query expiresAt value. */
|
|
126
|
+
selectQueryExpiresAt: <QK_6 extends keyof QP | keyof QR>(state: unknown, query: QK_6, cacheKey: Key) => number | undefined;
|
|
103
127
|
/** Selects mutation state. */
|
|
104
|
-
selectMutationState: <MK_1 extends keyof MP | keyof MR>(state: unknown, mutation: MK_1) =>
|
|
128
|
+
selectMutationState: <MK_1 extends keyof MP | keyof MR>(state: unknown, mutation: MK_1) => MutationState<MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never, MK_1 extends keyof MP & keyof MR ? MR[MK_1] : never>;
|
|
105
129
|
/** Selects mutation latest result. */
|
|
106
130
|
selectMutationResult: <MK_2 extends keyof MP | keyof MR>(state: unknown, mutation: MK_2) => (MK_2 extends keyof MP & keyof MR ? MR[MK_2] : never) | undefined;
|
|
107
131
|
/** Selects mutation loading state. */
|
|
@@ -120,20 +144,18 @@ export declare const createCache: <N extends string, T extends Typenames, QP, QR
|
|
|
120
144
|
hooks: {
|
|
121
145
|
/** Returns client object with query and mutate functions. */
|
|
122
146
|
useClient: () => {
|
|
123
|
-
query: <
|
|
147
|
+
query: <QK_7 extends keyof QP | keyof QR>(options: QueryOptions<T, QP, QR, QK_7>) => Promise<QueryResult<QK_7 extends keyof QP & keyof QR ? QR[QK_7] : never>>;
|
|
124
148
|
mutate: <MK_6 extends keyof MP | keyof MR>(options: {
|
|
125
149
|
mutation: MK_6;
|
|
126
150
|
params: MK_6 extends keyof MP & keyof MR ? MP[MK_6] : never;
|
|
127
151
|
}) => Promise<MutationResult<MK_6 extends keyof MP & keyof MR ? MR[MK_6] : never>>;
|
|
128
152
|
};
|
|
129
|
-
/** Fetches query when params change and subscribes to query state. */
|
|
130
|
-
useQuery: <
|
|
131
|
-
params: QK_7 extends keyof QP & keyof QR ? QP[QK_7] : never;
|
|
132
|
-
} | undefined) => Promise<QueryResult<QK_7 extends infer T_5 ? T_5 extends QK_7 ? T_5 extends keyof QP & keyof QR ? QR[T_5] : never : never : never>>];
|
|
153
|
+
/** Fetches query when params change and subscribes to query state changes (except `expiresAt` field). */
|
|
154
|
+
useQuery: <QK_8 extends keyof QP | keyof QR>(options: import("./types").UseQueryOptions<T, QP, QR, QK_8>) => readonly [Omit<QueryState<QK_8 extends keyof QP & keyof QR ? QP[QK_8] : never, QK_8 extends keyof QP & keyof QR ? QR[QK_8] : never>, "expiresAt">, (options?: Partial<Pick<QueryOptions<T, QP, QR, QK_8>, "params" | "onlyIfExpired">> | undefined) => Promise<QueryResult<QK_8 extends infer T_5 ? T_5 extends QK_8 ? T_5 extends keyof QP & keyof QR ? QR[T_5] : never : never : never>>];
|
|
133
155
|
/** Subscribes to provided mutation state and provides mutate function. */
|
|
134
156
|
useMutation: <MK_7 extends keyof MP | keyof MR>(options: {
|
|
135
157
|
mutation: MK_7;
|
|
136
|
-
}) => readonly [(params: MK_7 extends keyof MP & keyof MR ? MP[MK_7] : never) => Promise<MutationResult<MK_7 extends infer T_6 ? T_6 extends MK_7 ? T_6 extends keyof MP & keyof MR ? MR[T_6] : never : never : never>>,
|
|
158
|
+
}) => readonly [(params: MK_7 extends keyof MP & keyof MR ? MP[MK_7] : never) => Promise<MutationResult<MK_7 extends infer T_6 ? T_6 extends MK_7 ? T_6 extends keyof MP & keyof MR ? MR[T_6] : never : never : never>>, MutationState<MK_7 extends keyof MP & keyof MR ? MP[MK_7] : never, MK_7 extends keyof MP & keyof MR ? MP[MK_7] : never>, () => boolean];
|
|
137
159
|
/** useSelector + selectEntityById. */
|
|
138
160
|
useSelectEntityById: <TN_2 extends keyof T>(id: Key | null | undefined, typename: TN_2) => T[TN_2] | undefined;
|
|
139
161
|
};
|
package/dist/createCache.js
CHANGED
|
@@ -4,9 +4,9 @@ exports.createCache = void 0;
|
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const react_redux_1 = require("react-redux");
|
|
6
6
|
const createActions_1 = require("./createActions");
|
|
7
|
+
const createCacheReducer_1 = require("./createCacheReducer");
|
|
7
8
|
const mutate_1 = require("./mutate");
|
|
8
9
|
const query_1 = require("./query");
|
|
9
|
-
const reducer_1 = require("./reducer");
|
|
10
10
|
const useMutation_1 = require("./useMutation");
|
|
11
11
|
const useQuery_1 = require("./useQuery");
|
|
12
12
|
const utilsAndConstants_1 = require("./utilsAndConstants");
|
|
@@ -48,7 +48,7 @@ const createCache = (partialCache) => {
|
|
|
48
48
|
/** Keeps all options, passed while creating the cache. */
|
|
49
49
|
cache,
|
|
50
50
|
/** Reducer of the cache, should be added to redux store. */
|
|
51
|
-
reducer: (0,
|
|
51
|
+
reducer: (0, createCacheReducer_1.createCacheReducer)(actions, cache.typenames, Object.keys(cache.queries), cache.options),
|
|
52
52
|
actions,
|
|
53
53
|
selectors: {
|
|
54
54
|
/** Selects query state. */
|
|
@@ -69,6 +69,10 @@ const createCache = (partialCache) => {
|
|
|
69
69
|
selectQueryParams: (state, query, cacheKey) => {
|
|
70
70
|
return selectQueryState(state, query, cacheKey).params;
|
|
71
71
|
},
|
|
72
|
+
/** Selects query expiresAt value. */
|
|
73
|
+
selectQueryExpiresAt: (state, query, cacheKey) => {
|
|
74
|
+
return selectQueryState(state, query, cacheKey).expiresAt;
|
|
75
|
+
},
|
|
72
76
|
/** Selects mutation state. */
|
|
73
77
|
selectMutationState,
|
|
74
78
|
/** Selects mutation latest result. */
|
|
@@ -106,11 +110,13 @@ const createCache = (partialCache) => {
|
|
|
106
110
|
const client = {
|
|
107
111
|
query: (options) => {
|
|
108
112
|
var _a;
|
|
109
|
-
const { query: queryKey, params } = options;
|
|
113
|
+
const { query: queryKey, params, onlyIfExpired, secondsToLive, mergeResults } = options;
|
|
110
114
|
const getCacheKey = (_a = cache.queries[queryKey].getCacheKey) !== null && _a !== void 0 ? _a : (utilsAndConstants_1.defaultGetCacheKey);
|
|
111
115
|
// @ts-expect-error fix later
|
|
112
116
|
const cacheKey = getCacheKey(params);
|
|
113
|
-
return (0, query_1.query)('query', store, cache, actions, queryKey, cacheKey, params
|
|
117
|
+
return (0, query_1.query)('query', store, cache, actions, queryKey, cacheKey, params, secondsToLive, onlyIfExpired,
|
|
118
|
+
// @ts-expect-error fix later
|
|
119
|
+
mergeResults);
|
|
114
120
|
},
|
|
115
121
|
mutate: (options) => {
|
|
116
122
|
return (0, mutate_1.mutate)('mutate', store, cache, actions, options.mutation, options.params, abortControllers);
|
|
@@ -119,7 +125,7 @@ const createCache = (partialCache) => {
|
|
|
119
125
|
return client;
|
|
120
126
|
}, [store]);
|
|
121
127
|
},
|
|
122
|
-
/** Fetches query when params change and subscribes to query state. */
|
|
128
|
+
/** Fetches query when params change and subscribes to query state changes (except `expiresAt` field). */
|
|
123
129
|
useQuery: (options) => (0, useQuery_1.useQuery)(cache, actions, options),
|
|
124
130
|
/** Subscribes to provided mutation state and provides mutate function. */
|
|
125
131
|
useMutation: (options) => (0, useMutation_1.useMutation)(cache, actions, options, abortControllers),
|
|
@@ -1,27 +1,34 @@
|
|
|
1
1
|
import type { ActionMap } from './createActions';
|
|
2
|
-
import type { CacheOptions, Dict, EntitiesMap,
|
|
2
|
+
import type { CacheOptions, Dict, EntitiesMap, MutationState, QueryState, Typenames } from './types';
|
|
3
3
|
export type ReduxCacheState<T extends Typenames, QP, QR, MP, MR> = ReturnType<ReturnType<typeof createCacheReducer<string, T, QP, QR, MP, MR>>>;
|
|
4
4
|
export declare const createCacheReducer: <N extends string, T extends Typenames, QP, QR, MP, MR>(actions: ActionMap<N, T, QP, QR, MP, MR>, typenames: T, queryKeys: (keyof QP & keyof QR)[], cacheOptions: CacheOptions) => (state: {
|
|
5
5
|
entities: EntitiesMap<T>;
|
|
6
|
-
queries: QP | QR extends infer T_1 ? { [QK in keyof T_1]: Dict<
|
|
7
|
-
mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]:
|
|
6
|
+
queries: QP | QR extends infer T_1 ? { [QK in keyof T_1]: Dict<QueryState<QP[QK], QR[QK]> | undefined>; } : never;
|
|
7
|
+
mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]: MutationState<MP[MK], MR[MK]>; } : never;
|
|
8
8
|
} | undefined, action: {
|
|
9
9
|
type: `@rrc/${N}/updateQueryStateAndEntities`;
|
|
10
10
|
queryKey: keyof QP & keyof QR;
|
|
11
11
|
queryCacheKey: import("./types").Key;
|
|
12
|
-
state: Partial<
|
|
12
|
+
state: Partial<QueryState<QP[keyof QP & keyof QR], QR[keyof QP & keyof QR]>> | undefined;
|
|
13
13
|
entityChanges: import("./types").EntityChanges<T> | undefined;
|
|
14
14
|
} | {
|
|
15
15
|
type: `@rrc/${N}/updateMutationStateAndEntities`;
|
|
16
16
|
mutationKey: keyof MP & keyof MR;
|
|
17
|
-
state: Partial<
|
|
17
|
+
state: Partial<MutationState<MP[keyof MP & keyof MR], MR[keyof MP & keyof MR]>> | undefined;
|
|
18
18
|
entityChanges: import("./types").EntityChanges<T> | undefined;
|
|
19
19
|
} | {
|
|
20
20
|
type: `@rrc/${N}/mergeEntityChanges`;
|
|
21
21
|
changes: import("./types").EntityChanges<T>;
|
|
22
|
+
} | {
|
|
23
|
+
type: `@rrc/${N}/invalidateQuery`;
|
|
24
|
+
queries: {
|
|
25
|
+
key: keyof QP & keyof QR;
|
|
26
|
+
cacheKey?: import("./types").Key | undefined;
|
|
27
|
+
expiresAt?: number | undefined;
|
|
28
|
+
}[];
|
|
22
29
|
} | {
|
|
23
30
|
type: `@rrc/${N}/clearQueryState`;
|
|
24
|
-
|
|
31
|
+
queries: {
|
|
25
32
|
key: keyof QP & keyof QR;
|
|
26
33
|
cacheKey?: import("./types").Key | undefined;
|
|
27
34
|
}[];
|
|
@@ -30,6 +37,6 @@ export declare const createCacheReducer: <N extends string, T extends Typenames,
|
|
|
30
37
|
mutationKeys: (keyof MP & keyof MR)[];
|
|
31
38
|
}) => {
|
|
32
39
|
entities: EntitiesMap<T>;
|
|
33
|
-
queries: QP | QR extends infer T_1 ? { [QK in keyof T_1]: Dict<
|
|
34
|
-
mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]:
|
|
40
|
+
queries: QP | QR extends infer T_1 ? { [QK in keyof T_1]: Dict<QueryState<QP[QK], QR[QK]> | undefined>; } : never;
|
|
41
|
+
mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]: MutationState<MP[MK], MR[MK]>; } : never;
|
|
35
42
|
};
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createCacheReducer = void 0;
|
|
4
4
|
const utilsAndConstants_1 = require("./utilsAndConstants");
|
|
5
5
|
const EMPTY_QUERY_STATE = Object.freeze({});
|
|
6
|
+
const optionalQueryKeys = ['error', 'expiresAt', 'result', 'params'];
|
|
7
|
+
const optionalMutationKeys = ['error', 'result', 'params'];
|
|
6
8
|
const createCacheReducer = (actions, typenames, queryKeys, cacheOptions) => {
|
|
7
9
|
const entitiesMap = {};
|
|
8
10
|
for (const key in typenames) {
|
|
@@ -32,6 +34,14 @@ const createCacheReducer = (actions, typenames, queryKeys, cacheOptions) => {
|
|
|
32
34
|
const { queryKey, queryCacheKey, state: queryState, entityChanges, } = action;
|
|
33
35
|
const oldQueryState = (_a = state.queries[queryKey][queryCacheKey]) !== null && _a !== void 0 ? _a : utilsAndConstants_1.DEFAULT_QUERY_MUTATION_STATE;
|
|
34
36
|
let newQueryState = queryState && Object.assign(Object.assign({}, oldQueryState), queryState);
|
|
37
|
+
// remove undefined optional fields
|
|
38
|
+
if (newQueryState) {
|
|
39
|
+
for (const key of optionalQueryKeys) {
|
|
40
|
+
if (key in newQueryState && newQueryState[key] === undefined) {
|
|
41
|
+
delete newQueryState[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
35
45
|
if (deepEqual === null || deepEqual === void 0 ? void 0 : deepEqual(oldQueryState, newQueryState)) {
|
|
36
46
|
newQueryState = undefined;
|
|
37
47
|
}
|
|
@@ -51,6 +61,14 @@ const createCacheReducer = (actions, typenames, queryKeys, cacheOptions) => {
|
|
|
51
61
|
const { mutationKey, state: mutationState, entityChanges, } = action;
|
|
52
62
|
const oldMutationState = (_b = state.mutations[mutationKey]) !== null && _b !== void 0 ? _b : utilsAndConstants_1.DEFAULT_QUERY_MUTATION_STATE;
|
|
53
63
|
let newMutationState = mutationState && Object.assign(Object.assign({}, oldMutationState), mutationState);
|
|
64
|
+
// remove undefined optional fields
|
|
65
|
+
if (newMutationState) {
|
|
66
|
+
for (const key of optionalMutationKeys) {
|
|
67
|
+
if (key in newMutationState && newMutationState[key] === undefined) {
|
|
68
|
+
delete newMutationState[key];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
54
72
|
if (deepEqual === null || deepEqual === void 0 ? void 0 : deepEqual(oldMutationState, newMutationState)) {
|
|
55
73
|
newMutationState = undefined;
|
|
56
74
|
}
|
|
@@ -71,26 +89,71 @@ const createCacheReducer = (actions, typenames, queryKeys, cacheOptions) => {
|
|
|
71
89
|
const newEntities = (0, utilsAndConstants_1.applyEntityChanges)(state.entities, changes, cacheOptions);
|
|
72
90
|
return newEntities ? Object.assign(Object.assign({}, state), { entities: newEntities }) : state;
|
|
73
91
|
}
|
|
92
|
+
case actions.invalidateQuery.type: {
|
|
93
|
+
const { queries: queriesToInvalidate } = action;
|
|
94
|
+
if (!queriesToInvalidate.length) {
|
|
95
|
+
return state;
|
|
96
|
+
}
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
let newQueries = undefined;
|
|
99
|
+
for (const { key, cacheKey, expiresAt = now } of queriesToInvalidate) {
|
|
100
|
+
const queryStates = (newQueries !== null && newQueries !== void 0 ? newQueries : state.queries)[key];
|
|
101
|
+
if (cacheKey != null) {
|
|
102
|
+
if (queryStates[cacheKey]) {
|
|
103
|
+
const queryState = queryStates[cacheKey];
|
|
104
|
+
if (queryState && queryState.expiresAt !== expiresAt) {
|
|
105
|
+
newQueries !== null && newQueries !== void 0 ? newQueries : (newQueries = Object.assign({}, state.queries));
|
|
106
|
+
if (state.queries[key] === newQueries[key]) {
|
|
107
|
+
newQueries[key] = Object.assign({}, newQueries[key]);
|
|
108
|
+
}
|
|
109
|
+
// @ts-expect-error fix type later
|
|
110
|
+
newQueries[key][cacheKey] = Object.assign(Object.assign({}, queryState), { expiresAt });
|
|
111
|
+
if (expiresAt === undefined) {
|
|
112
|
+
delete newQueries[key][cacheKey].expiresAt;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
for (const cacheKey in queryStates) {
|
|
119
|
+
const queryState = queryStates[cacheKey];
|
|
120
|
+
if (queryState && queryState.expiresAt !== expiresAt) {
|
|
121
|
+
newQueries !== null && newQueries !== void 0 ? newQueries : (newQueries = Object.assign({}, state.queries));
|
|
122
|
+
if (state.queries[key] === newQueries[key]) {
|
|
123
|
+
newQueries[key] = Object.assign({}, newQueries[key]);
|
|
124
|
+
}
|
|
125
|
+
newQueries[key][cacheKey] = Object.assign(Object.assign({}, queryState), { expiresAt });
|
|
126
|
+
if (expiresAt === undefined) {
|
|
127
|
+
delete newQueries[key][cacheKey].expiresAt;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return !newQueries
|
|
134
|
+
? state
|
|
135
|
+
: Object.assign(Object.assign({}, state), { queries: newQueries });
|
|
136
|
+
}
|
|
74
137
|
case actions.clearQueryState.type: {
|
|
75
|
-
const {
|
|
76
|
-
if (!
|
|
138
|
+
const { queries: queriesToClear } = action;
|
|
139
|
+
if (!queriesToClear.length) {
|
|
77
140
|
return state;
|
|
78
141
|
}
|
|
79
142
|
let newQueries = undefined;
|
|
80
|
-
for (const
|
|
81
|
-
const
|
|
82
|
-
if (
|
|
83
|
-
if (
|
|
143
|
+
for (const { key, cacheKey } of queriesToClear) {
|
|
144
|
+
const queryStates = (newQueries !== null && newQueries !== void 0 ? newQueries : state.queries)[key];
|
|
145
|
+
if (cacheKey != null) {
|
|
146
|
+
if (queryStates[cacheKey]) {
|
|
84
147
|
newQueries !== null && newQueries !== void 0 ? newQueries : (newQueries = Object.assign({}, state.queries));
|
|
85
|
-
if (state.queries[
|
|
86
|
-
newQueries[
|
|
148
|
+
if (state.queries[key] === newQueries[key]) {
|
|
149
|
+
newQueries[key] = Object.assign({}, newQueries[key]);
|
|
87
150
|
}
|
|
88
|
-
delete newQueries[
|
|
151
|
+
delete newQueries[key][cacheKey];
|
|
89
152
|
}
|
|
90
153
|
}
|
|
91
|
-
else if (
|
|
154
|
+
else if (queryStates !== EMPTY_QUERY_STATE) {
|
|
92
155
|
newQueries !== null && newQueries !== void 0 ? newQueries : (newQueries = Object.assign({}, state.queries));
|
|
93
|
-
newQueries[
|
|
156
|
+
newQueries[key] = EMPTY_QUERY_STATE;
|
|
94
157
|
}
|
|
95
158
|
}
|
|
96
159
|
return !newQueries
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { createCache } from './createCache';
|
|
2
|
-
export type { ReduxCacheState } from './
|
|
2
|
+
export type { ReduxCacheState } from './createCacheReducer';
|
|
3
3
|
export * from './types';
|
|
4
4
|
export { defaultGetCacheKey, DEFAULT_QUERY_MUTATION_STATE as defaultQueryMutationState, } from './utilsAndConstants';
|
package/dist/index.js
CHANGED
|
@@ -22,18 +22,20 @@ var utilsAndConstants_1 = require("./utilsAndConstants");
|
|
|
22
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.DEFAULT_QUERY_MUTATION_STATE; } });
|
|
24
24
|
// Backlog
|
|
25
|
-
// ! high
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
25
|
+
// ! high (1.0.0)
|
|
26
|
+
// key -> query
|
|
27
|
+
// rca -> vite
|
|
28
|
+
// defaults
|
|
29
|
+
// remove cachePolicy? make skip/enabled a function? skip -> enabled/shouldFetch?
|
|
30
|
+
// optional typenames: {} as Typenames
|
|
31
|
+
// remove mergeResults? bcs store is passed to queries/mutations
|
|
32
|
+
// remove undefined optional fields & emtpy states
|
|
33
|
+
// generate full api docs
|
|
32
34
|
// ! medium
|
|
35
|
+
// optimistic response
|
|
36
|
+
// make query key / cache key difference more clear in the docs
|
|
33
37
|
// check type of function arguments in dev
|
|
34
38
|
// allow multiple mutation with same keys?
|
|
35
|
-
// type extractors from cache
|
|
36
|
-
// custom useStore
|
|
37
39
|
// return back deserialize selector?
|
|
38
40
|
// selector for entities by typename
|
|
39
41
|
// callback option on error / success?
|
|
@@ -42,6 +44,7 @@ Object.defineProperty(exports, "defaultQueryMutationState", { enumerable: true,
|
|
|
42
44
|
// deep equal entities while merging state
|
|
43
45
|
// make error type generic
|
|
44
46
|
// ! low
|
|
47
|
+
// custom useStore & useSelector to support multiple stores?
|
|
45
48
|
// access to currently loading queries and mutations?
|
|
46
49
|
// add params to the state?
|
|
47
50
|
// cancellation to queries
|
package/dist/mutate.js
CHANGED
|
@@ -44,7 +44,7 @@ const mutate = (logTag, store, cache, { updateMutationStateAndEntities, }, mutat
|
|
|
44
44
|
try {
|
|
45
45
|
response = yield fetchFn(
|
|
46
46
|
// @ts-expect-error fix later
|
|
47
|
-
params, abortController.signal);
|
|
47
|
+
params, store, abortController.signal);
|
|
48
48
|
}
|
|
49
49
|
catch (e) {
|
|
50
50
|
error = e;
|
|
@@ -67,12 +67,12 @@ const mutate = (logTag, store, cache, { updateMutationStateAndEntities, }, mutat
|
|
|
67
67
|
return { error };
|
|
68
68
|
}
|
|
69
69
|
if (response) {
|
|
70
|
-
|
|
70
|
+
const newState = {
|
|
71
71
|
error: undefined,
|
|
72
72
|
loading: false,
|
|
73
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
74
73
|
result: response.result,
|
|
75
|
-
}
|
|
74
|
+
};
|
|
75
|
+
store.dispatch(updateMutationStateAndEntities(mutationKey, newState, response));
|
|
76
76
|
// @ts-expect-error fix later
|
|
77
77
|
return { result: response.result };
|
|
78
78
|
}
|
package/dist/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Store } from 'redux';
|
|
2
2
|
import type { ActionMap } from './createActions';
|
|
3
3
|
import type { Cache, Key, QueryResult, Typenames } from './types';
|
|
4
|
-
export declare const query: <N extends string, T extends Typenames, QP, QR, MP, MR, QK extends keyof QP | keyof QR>(logTag: string, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, { updateQueryStateAndEntities, }: Pick<ActionMap<N, T, QP, QR, unknown, unknown>, "updateQueryStateAndEntities">, queryKey: QK, cacheKey: Key, params: QK extends keyof QP & keyof QR ? QP[QK] : never) => Promise<QueryResult<QK extends keyof QP & keyof QR ? QR[QK] : never>>;
|
|
4
|
+
export declare const query: <N extends string, T extends Typenames, QP, QR, MP, MR, QK extends keyof QP | keyof QR>(logTag: string, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, { updateQueryStateAndEntities, }: Pick<ActionMap<N, T, QP, QR, unknown, unknown>, "updateQueryStateAndEntities">, queryKey: QK, cacheKey: Key, params: QK extends keyof QP & keyof QR ? QP[QK] : never, secondsToLive: number | undefined, onlyIfExpired: boolean | undefined, mergeResults?: ((oldResult: QR[keyof QP & keyof QR & string] | undefined, response: import("./types").QueryResponse<T, QR[keyof QP & keyof QR & string]>, params: QP[keyof QP & keyof QR & string] | undefined, store: Store<any, import("redux").AnyAction>) => QR[keyof QP & keyof QR & string]) | ((oldResult: QR[keyof QP & keyof QR & number] | undefined, response: import("./types").QueryResponse<T, QR[keyof QP & keyof QR & number]>, params: QP[keyof QP & keyof QR & number] | undefined, store: Store<any, import("redux").AnyAction>) => QR[keyof QP & keyof QR & number]) | ((oldResult: QR[keyof QP & keyof QR & symbol] | undefined, response: import("./types").QueryResponse<T, QR[keyof QP & keyof QR & symbol]>, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store<any, import("redux").AnyAction>) => QR[keyof QP & keyof QR & symbol]) | undefined) => Promise<QueryResult<QK extends keyof QP & keyof QR ? QR[QK] : never>>;
|
package/dist/query.js
CHANGED
|
@@ -11,11 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.query = void 0;
|
|
13
13
|
const utilsAndConstants_1 = require("./utilsAndConstants");
|
|
14
|
-
const query = (logTag, store, cache, { updateQueryStateAndEntities, }, queryKey, cacheKey, params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
-
var _a;
|
|
14
|
+
const query = (logTag, store, cache, { updateQueryStateAndEntities, }, queryKey, cacheKey, params, secondsToLive = cache.queries[queryKey].secondsToLive, onlyIfExpired, mergeResults = cache.queries[queryKey].mergeResults) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
var _a, _b;
|
|
16
16
|
const logsEnabled = cache.options.logsEnabled;
|
|
17
17
|
const cacheStateSelector = cache.cacheStateSelector;
|
|
18
|
-
const mergeResults = cache.queries[queryKey].mergeResults;
|
|
19
18
|
const queryStateOnStart = cacheStateSelector(store.getState()).queries[queryKey][cacheKey];
|
|
20
19
|
if (queryStateOnStart === null || queryStateOnStart === void 0 ? void 0 : queryStateOnStart.loading) {
|
|
21
20
|
logsEnabled &&
|
|
@@ -26,17 +25,27 @@ const query = (logTag, store, cache, { updateQueryStateAndEntities, }, queryKey,
|
|
|
26
25
|
});
|
|
27
26
|
return CANCELLED_RESULT;
|
|
28
27
|
}
|
|
28
|
+
if (onlyIfExpired && (queryStateOnStart === null || queryStateOnStart === void 0 ? void 0 : queryStateOnStart.expiresAt) != null && queryStateOnStart.expiresAt > Date.now()) {
|
|
29
|
+
logsEnabled &&
|
|
30
|
+
(0, utilsAndConstants_1.log)(`${logTag} cancelled: not expired yet`, {
|
|
31
|
+
queryStateOnStart,
|
|
32
|
+
params,
|
|
33
|
+
cacheKey,
|
|
34
|
+
onlyIfExpired,
|
|
35
|
+
});
|
|
36
|
+
return CANCELLED_RESULT;
|
|
37
|
+
}
|
|
29
38
|
store.dispatch(updateQueryStateAndEntities(queryKey, cacheKey, {
|
|
30
39
|
loading: true,
|
|
31
40
|
params,
|
|
32
41
|
}));
|
|
33
|
-
logsEnabled && (0, utilsAndConstants_1.log)(`${logTag} started`, {
|
|
42
|
+
logsEnabled && (0, utilsAndConstants_1.log)(`${logTag} started`, { queryKey, params, cacheKey, queryStateOnStart, onlyIfExpired });
|
|
34
43
|
let response;
|
|
35
44
|
const fetchFn = cache.queries[queryKey].query;
|
|
36
45
|
try {
|
|
37
46
|
response = yield fetchFn(
|
|
38
47
|
// @ts-expect-error fix later
|
|
39
|
-
params);
|
|
48
|
+
params, store);
|
|
40
49
|
}
|
|
41
50
|
catch (error) {
|
|
42
51
|
store.dispatch(updateQueryStateAndEntities(queryKey, cacheKey, {
|
|
@@ -48,10 +57,11 @@ const query = (logTag, store, cache, { updateQueryStateAndEntities, }, queryKey,
|
|
|
48
57
|
const newState = {
|
|
49
58
|
error: undefined,
|
|
50
59
|
loading: false,
|
|
60
|
+
expiresAt: (_a = response.expiresAt) !== null && _a !== void 0 ? _a : (secondsToLive != null ? Date.now() + secondsToLive * 1000 : undefined),
|
|
51
61
|
result: mergeResults
|
|
52
62
|
? mergeResults(
|
|
53
63
|
// @ts-expect-error fix later
|
|
54
|
-
(
|
|
64
|
+
(_b = cacheStateSelector(store.getState()).queries[queryKey][cacheKey]) === null || _b === void 0 ? void 0 : _b.result, response, params, store)
|
|
55
65
|
: response.result,
|
|
56
66
|
};
|
|
57
67
|
store.dispatch(updateQueryStateAndEntities(queryKey, cacheKey, newState, response));
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Store } from 'redux';
|
|
2
|
-
import type { ReduxCacheState } from './
|
|
2
|
+
import type { ReduxCacheState } from './createCacheReducer';
|
|
3
3
|
export type Key = string | number | symbol;
|
|
4
4
|
export type Dict<T> = Record<Key, T>;
|
|
5
5
|
export type OptionalPartial<T, K extends keyof T> = Partial<{
|
|
@@ -69,14 +69,20 @@ export type EntitiesMap<T extends Typenames> = {
|
|
|
69
69
|
export type EntityIds<T extends Typenames> = {
|
|
70
70
|
[K in keyof T]?: Key[];
|
|
71
71
|
};
|
|
72
|
-
export type Query<T extends Typenames
|
|
72
|
+
export type Query<P, T extends Typenames = Typenames, R = unknown> = (
|
|
73
|
+
/** Query parameters */
|
|
74
|
+
params: P,
|
|
75
|
+
/** Redux store */
|
|
76
|
+
store: Store) => Promise<QueryResponse<T, R>>;
|
|
73
77
|
export type QueryInfo<T extends Typenames, P, R> = {
|
|
74
|
-
query: Query<
|
|
78
|
+
query: Query<P, T, R>;
|
|
75
79
|
/**
|
|
76
80
|
* Cache policy.
|
|
77
81
|
* @default cache-first
|
|
78
82
|
*/
|
|
79
83
|
cachePolicy?: QueryCachePolicy;
|
|
84
|
+
/** If set, this value updates expiresAt value of query state when query resut is received. */
|
|
85
|
+
secondsToLive?: number;
|
|
80
86
|
/** Merges results before saving to the store. Default implementation is using the latest result. */
|
|
81
87
|
mergeResults?: (oldResult: R | undefined, response: QueryResponse<T, R>, params: P | undefined, store: Store) => R;
|
|
82
88
|
/**
|
|
@@ -86,34 +92,45 @@ export type QueryInfo<T extends Typenames, P, R> = {
|
|
|
86
92
|
* */
|
|
87
93
|
getCacheKey?: (params?: P) => Key;
|
|
88
94
|
};
|
|
95
|
+
export type QueryState<P, R> = MutationState<P, R> & {
|
|
96
|
+
expiresAt?: number;
|
|
97
|
+
};
|
|
89
98
|
export type UseQueryOptions<T extends Typenames, QP, QR, QK extends keyof (QP & QR)> = {
|
|
90
99
|
query: QK;
|
|
91
100
|
params: QK extends keyof (QP | QR) ? QP[QK] : never;
|
|
101
|
+
/** When true fetch is not performed. When switches to false fetch is performed depending on cache policy. */
|
|
92
102
|
skip?: boolean;
|
|
93
|
-
} & Pick<QueryInfo<T,
|
|
103
|
+
} & Pick<QueryInfo<T, QK extends keyof (QP | QR) ? QP[QK] : never, QK extends keyof (QP | QR) ? QR[QK] : never>, 'cachePolicy' | 'secondsToLive' | 'mergeResults'>;
|
|
94
104
|
/**
|
|
95
105
|
* @param cache-first for each params key fetch is not called if cache exists.
|
|
96
106
|
* @param cache-and-fetch for each params key result is taken from cache and fetch is called.
|
|
97
107
|
*/
|
|
98
108
|
export type QueryCachePolicy = 'cache-first' | 'cache-and-fetch';
|
|
99
109
|
export type QueryResponse<T extends Typenames, R> = EntityChanges<T> & {
|
|
100
|
-
/** Normalized result of a query. */
|
|
101
110
|
result: R;
|
|
111
|
+
/** If defined, overrides this value for query state, ignoring `secondsToLive`. */
|
|
112
|
+
expiresAt?: number;
|
|
102
113
|
};
|
|
103
114
|
export type QueryResult<R> = {
|
|
104
115
|
error?: unknown;
|
|
105
116
|
cancelled?: true;
|
|
106
117
|
result?: R;
|
|
107
118
|
};
|
|
108
|
-
export type QueryOptions<T extends Typenames, QP, QR, QK extends keyof (QP & QR)> = Omit<UseQueryOptions<T, QP, QR, QK>, 'skip'
|
|
109
|
-
|
|
119
|
+
export type QueryOptions<T extends Typenames, QP, QR, QK extends keyof (QP & QR)> = Omit<UseQueryOptions<T, QP, QR, QK>, 'skip' | 'cachePolicy'> & {
|
|
120
|
+
/** If set to true, query will run only if it is expired or result not yet cached. */
|
|
121
|
+
onlyIfExpired?: boolean;
|
|
122
|
+
};
|
|
123
|
+
export type Mutation<P, T extends Typenames = Typenames, R = unknown> = (
|
|
124
|
+
/** Mutation parameters */
|
|
125
|
+
params: P,
|
|
126
|
+
/** Redux store */
|
|
127
|
+
store: Store,
|
|
110
128
|
/** Signal is aborted for current mutation when the same mutation was called once again. */
|
|
111
129
|
abortSignal: AbortSignal) => Promise<MutationResponse<T, R>>;
|
|
112
130
|
export type MutationInfo<T extends Typenames, P, R> = {
|
|
113
|
-
mutation: Mutation<
|
|
131
|
+
mutation: Mutation<P, T, R>;
|
|
114
132
|
};
|
|
115
133
|
export type MutationResponse<T extends Typenames, R> = EntityChanges<T> & {
|
|
116
|
-
/** Normalized result of a mutation. */
|
|
117
134
|
result?: R;
|
|
118
135
|
};
|
|
119
136
|
export type MutationResult<R> = {
|
|
@@ -121,7 +138,7 @@ export type MutationResult<R> = {
|
|
|
121
138
|
aborted?: true;
|
|
122
139
|
result?: R;
|
|
123
140
|
};
|
|
124
|
-
export type
|
|
141
|
+
export type MutationState<P, R> = {
|
|
125
142
|
/** `true` when query or mutation is currently in progress. */
|
|
126
143
|
loading: boolean;
|
|
127
144
|
/** Result of the latest successfull response. */
|
package/dist/useMutation.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Store } from 'redux';
|
|
2
2
|
import { ActionMap } from './createActions';
|
|
3
|
-
import { Cache, Key,
|
|
3
|
+
import { Cache, Key, MutationState, Typenames } from './types';
|
|
4
4
|
export declare const useMutation: <N extends string, T extends Typenames, MP, MR, MK extends keyof MP | keyof MR>(cache: Cache<N, T, unknown, unknown, MP, MR>, actions: Pick<ActionMap<N, T, unknown, unknown, MP, MR>, "updateMutationStateAndEntities">, options: {
|
|
5
5
|
mutation: MK;
|
|
6
|
-
}, abortControllers: WeakMap<Store, Record<Key, AbortController>>) => readonly [(params: MK extends keyof MP & keyof MR ? MP[MK] : never) => Promise<import("./types").MutationResult<MK extends infer T_1 ? T_1 extends MK ? T_1 extends keyof MP & keyof MR ? MR[T_1] : never : never : never>>,
|
|
6
|
+
}, abortControllers: WeakMap<Store, Record<Key, AbortController>>) => readonly [(params: MK extends keyof MP & keyof MR ? MP[MK] : never) => Promise<import("./types").MutationResult<MK extends infer T_1 ? T_1 extends MK ? T_1 extends keyof MP & keyof MR ? MR[T_1] : never : never : never>>, MutationState<MK extends keyof MP & keyof MR ? MP[MK] : never, MK extends keyof MP & keyof MR ? MP[MK] : never>, () => boolean];
|
package/dist/useQuery.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import { ActionMap } from './createActions';
|
|
2
|
-
import { Cache,
|
|
3
|
-
export declare const useQuery: <N extends string, T extends Typenames, QP, QR, MP, MR, QK extends keyof QP | keyof QR>(cache: Cache<N, T, QP, QR, MP, MR>, actions: Pick<ActionMap<N, T, QP, QR, MP, MR>, "updateQueryStateAndEntities">, options: UseQueryOptions<T, QP, QR, QK>) => readonly [
|
|
4
|
-
params: QK extends keyof QP & keyof QR ? QP[QK] : never;
|
|
5
|
-
} | undefined) => Promise<import("./types").QueryResult<QK extends infer T_1 ? T_1 extends QK ? T_1 extends keyof QP & keyof QR ? QR[T_1] : never : never : never>>];
|
|
2
|
+
import { Cache, QueryOptions, QueryState, Typenames, UseQueryOptions } from './types';
|
|
3
|
+
export declare const useQuery: <N extends string, T extends Typenames, QP, QR, MP, MR, QK extends keyof QP | keyof QR>(cache: Cache<N, T, QP, QR, MP, MR>, actions: Pick<ActionMap<N, T, QP, QR, MP, MR>, "updateQueryStateAndEntities">, options: UseQueryOptions<T, QP, QR, QK>) => readonly [Omit<QueryState<QK extends keyof QP & keyof QR ? QP[QK] : never, QK extends keyof QP & keyof QR ? QR[QK] : never>, "expiresAt">, (options?: Partial<Pick<QueryOptions<T, QP, QR, QK>, "params" | "onlyIfExpired">> | undefined) => Promise<import("./types").QueryResult<QK extends infer T_1 ? T_1 extends QK ? T_1 extends keyof QP & keyof QR ? QR[T_1] : never : never : never>>];
|
package/dist/useQuery.js
CHANGED
|
@@ -16,7 +16,7 @@ const query_1 = require("./query");
|
|
|
16
16
|
const utilsAndConstants_1 = require("./utilsAndConstants");
|
|
17
17
|
const useQuery = (cache, actions, 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', } = options;
|
|
19
|
+
const { query: queryKey, skip, params, secondsToLive, cachePolicy = (_a = cache.queries[queryKey].cachePolicy) !== null && _a !== void 0 ? _a : 'cache-first', mergeResults, } = options;
|
|
20
20
|
const logsEnabled = cache.options.logsEnabled;
|
|
21
21
|
const getCacheKey = (_b = cache.queries[queryKey].getCacheKey) !== null && _b !== void 0 ? _b : (utilsAndConstants_1.defaultGetCacheKey);
|
|
22
22
|
const cacheStateSelector = cache.cacheStateSelector;
|
|
@@ -25,9 +25,13 @@ const useQuery = (cache, actions, options) => {
|
|
|
25
25
|
const cacheKey = getCacheKey(params);
|
|
26
26
|
/** Fetch query with the new parameters, or refetch with the same if parameters not provided. */
|
|
27
27
|
const fetch = (0, react_1.useCallback)((options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
+
const paramsPassed = options && 'params' in options;
|
|
28
29
|
return yield (0, query_1.query)('useQuery.fetch', store, cache, actions, queryKey,
|
|
29
30
|
// @ts-expect-error fix later
|
|
30
|
-
|
|
31
|
+
paramsPassed ? getCacheKey(options.params) : cacheKey, paramsPassed ? options.params : params, // params type can also have null | undefined, thats why we don't check for it here
|
|
32
|
+
secondsToLive, options === null || options === void 0 ? void 0 : options.onlyIfExpired,
|
|
33
|
+
// @ts-expect-error fix later
|
|
34
|
+
mergeResults);
|
|
31
35
|
}),
|
|
32
36
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
33
37
|
[store, queryKey, cacheKey]);
|
|
@@ -35,16 +39,20 @@ const useQuery = (cache, actions, options) => {
|
|
|
35
39
|
const queryState = (_c = (0, react_redux_1.useSelector)((state) => {
|
|
36
40
|
const queryState = cacheStateSelector(state).queries[queryKey][cacheKey];
|
|
37
41
|
return queryState; // TODO proper type
|
|
38
|
-
})) !== null && _c !== void 0 ? _c : utilsAndConstants_1.DEFAULT_QUERY_MUTATION_STATE;
|
|
42
|
+
}, (useQueryStateComparer))) !== null && _c !== void 0 ? _c : utilsAndConstants_1.DEFAULT_QUERY_MUTATION_STATE;
|
|
39
43
|
(0, react_1.useEffect)(() => {
|
|
40
44
|
if (skip) {
|
|
41
45
|
logsEnabled && (0, utilsAndConstants_1.log)('useQuery.useEffect skip fetch', { skip, cacheKey });
|
|
42
46
|
return;
|
|
43
47
|
}
|
|
44
|
-
if (queryState.result != null &&
|
|
48
|
+
if (queryState.result != null &&
|
|
49
|
+
cachePolicy === 'cache-first' &&
|
|
50
|
+
(queryState.expiresAt == null || queryState.expiresAt > Date.now())) {
|
|
45
51
|
logsEnabled &&
|
|
46
|
-
(0, utilsAndConstants_1.log)('useQuery.useEffect
|
|
52
|
+
(0, utilsAndConstants_1.log)('useQuery.useEffect no fetch due to cache policy', {
|
|
47
53
|
result: queryState.result,
|
|
54
|
+
expiresAt: queryState.expiresAt,
|
|
55
|
+
now: Date.now(),
|
|
48
56
|
cachePolicy,
|
|
49
57
|
});
|
|
50
58
|
return;
|
|
@@ -61,3 +69,16 @@ const useQuery = (cache, actions, options) => {
|
|
|
61
69
|
return [queryState, fetch];
|
|
62
70
|
};
|
|
63
71
|
exports.useQuery = useQuery;
|
|
72
|
+
/** Omit `expiresAt` from comparison */
|
|
73
|
+
const useQueryStateComparer = (state1, state2) => {
|
|
74
|
+
if (state1 === state2) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (state1 === undefined || state2 === undefined) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return (state1.error === state2.error &&
|
|
81
|
+
state1.loading === state2.loading &&
|
|
82
|
+
state1.params === state2.params &&
|
|
83
|
+
state1.result === state2.result);
|
|
84
|
+
};
|
|
@@ -4,9 +4,9 @@ export declare const optionalUtils: {
|
|
|
4
4
|
deepEqual?: (a: any, b: any) => boolean;
|
|
5
5
|
};
|
|
6
6
|
export declare const IS_DEV: boolean;
|
|
7
|
-
export declare const DEFAULT_QUERY_MUTATION_STATE: {
|
|
8
|
-
|
|
9
|
-
}
|
|
7
|
+
export declare const DEFAULT_QUERY_MUTATION_STATE: Readonly<{
|
|
8
|
+
loading: false;
|
|
9
|
+
}>;
|
|
10
10
|
export declare const defaultGetCacheKey: <P = unknown>(params: P) => Key;
|
|
11
11
|
export declare const log: (tag: string, data?: unknown) => void;
|
|
12
12
|
export declare const applyEntityChanges: <T extends Typenames>(entities: EntitiesMap<T>, changes: EntityChanges<T>, options: CacheOptions) => EntitiesMap<T> | undefined;
|
|
@@ -20,7 +20,7 @@ exports.IS_DEV = (() => {
|
|
|
20
20
|
return process.env.NODE_ENV === 'development';
|
|
21
21
|
}
|
|
22
22
|
})();
|
|
23
|
-
exports.DEFAULT_QUERY_MUTATION_STATE = { loading: false };
|
|
23
|
+
exports.DEFAULT_QUERY_MUTATION_STATE = Object.freeze({ loading: false });
|
|
24
24
|
const defaultGetCacheKey = (params) => {
|
|
25
25
|
switch (typeof params) {
|
|
26
26
|
case 'string':
|
|
@@ -109,11 +109,9 @@ const applyEntityChanges = (entities, changes, options) => {
|
|
|
109
109
|
options.logsEnabled &&
|
|
110
110
|
(0, exports.log)('applyEntityChanges', {
|
|
111
111
|
entities,
|
|
112
|
-
|
|
113
|
-
changes: require('util').inspect(changes, { depth: 4 }),
|
|
112
|
+
changes,
|
|
114
113
|
options,
|
|
115
|
-
|
|
116
|
-
result: require('util').inspect(result, { depth: 4 }),
|
|
114
|
+
result,
|
|
117
115
|
});
|
|
118
116
|
return result;
|
|
119
117
|
};
|
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.
|
|
5
|
+
"version": "0.7.0-rc.0",
|
|
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",
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"lint": "yarn eslint src",
|
|
13
13
|
"build": "yarn clean && yarn lint && tsc",
|
|
14
14
|
"test": "node node_modules/jest/bin/jest.js",
|
|
15
|
+
"preminor-rc": "yarn build && npm version preminor --preid rc",
|
|
16
|
+
"publish-rc": "npm publish --tag rc",
|
|
15
17
|
"prepublishOnly": "yarn build && yarn test"
|
|
16
18
|
},
|
|
17
19
|
"peerDependencies": {
|