react-redux-cache 0.6.1 → 0.7.0-rc.1

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
@@ -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, error: undefined, result: 2, params: 2},
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: { query: 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: number) => {
205
- const result = await ...
206
-
207
- const normalizedResult: {
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
- return normalizedResult
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: string) => {
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: number) => {
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 is used for denormalization and returns entities with proper types - User and Bank
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` when needed:
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. Second argument in mutations is `AbortSignal`, which can be used e.g. for cancelling http requests.
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.
@@ -1,23 +1,23 @@
1
- import type { EntityChanges, Key, QueryMutationState, Typenames } from './types';
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<QueryMutationState<QP[K], QR[K]>> | undefined, entityChanges?: EntityChanges<T> | undefined): {
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<QueryMutationState<QP[K], QR[K]>> | undefined;
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<QueryMutationState<MP[K_1], MR[K_1]>> | undefined, entityChanges?: EntityChanges<T> | undefined): {
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<QueryMutationState<MP[K_1], MR[K_1]>> | undefined;
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
- <K_2 extends keyof QP & keyof QR>(queryKeys: {
37
- key: K_2;
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
- queryKeys: {
42
- key: K_2;
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
- <K_3 extends keyof MP & keyof MR>(mutationKeys: K_3[]): {
76
+ <K_4 extends keyof MP & keyof MR>(mutationKeys: K_4[]): {
51
77
  type: `@rrc/${N}/clearMutationState`;
52
- mutationKeys: K_3[];
78
+ mutationKeys: K_4[];
53
79
  };
54
80
  type: `@rrc/${N}/clearMutationState`;
55
81
  };
@@ -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 = (queryKeys) => ({
37
+ const clearQueryState = (queries) => ({
32
38
  type: clearQueryStateType,
33
- queryKeys,
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,
@@ -1,35 +1,42 @@
1
- import type { Cache, Key, MutationResult, OptionalPartial, QueryMutationState, QueryOptions, QueryResult, Typenames } from './types';
1
+ import type { Cache, Key, MutateOptions, 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>, "options" | "queries" | "mutations" | "cacheStateSelector">) => {
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<QueryMutationState<QP[QK], QR[QK]>>; } : never;
15
- mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]: QueryMutationState<MP[MK], MR[MK]>; } : never;
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<QueryMutationState<QP[keyof QP & keyof QR], QR[keyof QP & keyof QR]>> | undefined;
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<QueryMutationState<MP[keyof MP & keyof MR], MR[keyof MP & keyof MR]>> | undefined;
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
- queryKeys: {
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<QueryMutationState<QP[QK], QR[QK]>>; } : never;
42
- mutations: MP | MR extends infer T_4 ? { [MK in keyof T_4]: QueryMutationState<MP[MK], MR[MK]>; } : never;
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<QueryMutationState<QP[K], QR[K]>> | undefined, entityChanges?: import("./types").EntityChanges<T> | undefined): {
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<QueryMutationState<QP[K], QR[K]>> | undefined;
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<QueryMutationState<MP[K_1], MR[K_1]>> | undefined, entityChanges?: import("./types").EntityChanges<T> | undefined): {
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<QueryMutationState<MP[K_1], MR[K_1]>> | undefined;
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
- clearQueryState: {
72
- <K_2 extends keyof QP & keyof QR>(queryKeys: {
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}/clearQueryState`;
77
- queryKeys: {
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
- <K_3 extends keyof MP & keyof MR>(mutationKeys: K_3[]): {
107
+ <K_4 extends keyof MP & keyof MR>(mutationKeys: K_4[]): {
86
108
  type: `@rrc/${N}/clearMutationState`;
87
- mutationKeys: K_3[];
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) => QueryMutationState<QK_1 extends keyof QP & keyof QR ? QP[QK_1] : never, QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never>;
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) => QueryMutationState<MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never, MK_1 extends keyof MP & keyof MR ? MR[MK_1] : never>;
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,13 @@ 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: <QK_6 extends keyof QP | keyof QR>(options: Omit<QueryOptions<T, QP, QR, QK_6>, "cachePolicy">) => Promise<QueryResult<QK_6 extends keyof QP & keyof QR ? QR[QK_6] : never>>;
124
- mutate: <MK_6 extends keyof MP | keyof MR>(options: {
125
- mutation: MK_6;
126
- params: MK_6 extends keyof MP & keyof MR ? MP[MK_6] : never;
127
- }) => Promise<MutationResult<MK_6 extends keyof MP & keyof MR ? MR[MK_6] : never>>;
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>>;
148
+ mutate: <MK_6 extends keyof MP | keyof MR>(options: MutateOptions<T, MP, MR, MK_6>) => Promise<MutationResult<MK_6 extends keyof MP & keyof MR ? MR[MK_6] : never>>;
128
149
  };
129
- /** Fetches query when params change and subscribes to query state. */
130
- useQuery: <QK_7 extends keyof QP | keyof QR>(options: import("./types").UseQueryOptions<T, QP, QR, QK_7>) => readonly [QueryMutationState<QK_7 extends keyof QP & keyof QR ? QP[QK_7] : never, QK_7 extends keyof QP & keyof QR ? QR[QK_7] : never>, (options?: {
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>>];
150
+ /** Fetches query when params change and subscribes to query state changes (except `expiresAt` field). */
151
+ 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
152
  /** Subscribes to provided mutation state and provides mutate function. */
134
- useMutation: <MK_7 extends keyof MP | keyof MR>(options: {
135
- 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>>, QueryMutationState<MK_7 extends keyof MP & keyof MR ? MP[MK_7] : never, MK_7 extends keyof MP & keyof MR ? MP[MK_7] : never>, () => boolean];
153
+ useMutation: <MK_7 extends keyof MP | keyof MR>(options: Omit<MutateOptions<T, MP, MR, MK_7>, "params">) => 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
154
  /** useSelector + selectEntityById. */
138
155
  useSelectEntityById: <TN_2 extends keyof T>(id: Key | null | undefined, typename: TN_2) => T[TN_2] | undefined;
139
156
  };
@@ -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, reducer_1.createCacheReducer)(actions, cache.typenames, Object.keys(cache.queries), cache.options),
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. */
@@ -110,16 +114,20 @@ const createCache = (partialCache) => {
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, options.secondsToLive, options.onlyIfExpired,
118
+ // @ts-expect-error fix later
119
+ options.mergeResults, options.onCompleted, options.onSuccess, options.onError);
114
120
  },
115
121
  mutate: (options) => {
116
- return (0, mutate_1.mutate)('mutate', store, cache, actions, options.mutation, options.params, abortControllers);
122
+ return (0, mutate_1.mutate)('mutate', store, cache, actions, options.mutation, options.params, abortControllers,
123
+ // @ts-expect-error fix later
124
+ options.onCompleted, options.onSuccess, options.onError);
117
125
  },
118
126
  };
119
127
  return client;
120
128
  }, [store]);
121
129
  },
122
- /** Fetches query when params change and subscribes to query state. */
130
+ /** Fetches query when params change and subscribes to query state changes (except `expiresAt` field). */
123
131
  useQuery: (options) => (0, useQuery_1.useQuery)(cache, actions, options),
124
132
  /** Subscribes to provided mutation state and provides mutate function. */
125
133
  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, QueryMutationState, Typenames } from './types';
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<QueryMutationState<QP[QK], QR[QK]>>; } : never;
7
- mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]: QueryMutationState<MP[MK], MR[MK]>; } : never;
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<QueryMutationState<QP[keyof QP & keyof QR], QR[keyof QP & keyof QR]>> | undefined;
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<QueryMutationState<MP[keyof MP & keyof MR], MR[keyof MP & keyof MR]>> | undefined;
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
- queryKeys: {
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<QueryMutationState<QP[QK], QR[QK]>>; } : never;
34
- mutations: MP | MR extends infer T_2 ? { [MK in keyof T_2]: QueryMutationState<MP[MK], MR[MK]>; } : never;
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 { queryKeys: queryKeysToClear } = action;
76
- if (!queryKeysToClear.length) {
138
+ const { queries: queriesToClear } = action;
139
+ if (!queriesToClear.length) {
77
140
  return state;
78
141
  }
79
142
  let newQueries = undefined;
80
- for (const query of queryKeysToClear) {
81
- const queryState = (newQueries !== null && newQueries !== void 0 ? newQueries : state.queries)[query.key];
82
- if (query.cacheKey != null) {
83
- if (queryState[query.cacheKey]) {
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[query.key] === newQueries[query.key]) {
86
- newQueries[query.key] = Object.assign({}, newQueries[query.key]);
148
+ if (state.queries[key] === newQueries[key]) {
149
+ newQueries[key] = Object.assign({}, newQueries[key]);
87
150
  }
88
- delete newQueries[query.key][query.cacheKey];
151
+ delete newQueries[key][cacheKey];
89
152
  }
90
153
  }
91
- else if (queryState !== EMPTY_QUERY_STATE) {
154
+ else if (queryStates !== EMPTY_QUERY_STATE) {
92
155
  newQueries !== null && newQueries !== void 0 ? newQueries : (newQueries = Object.assign({}, state.queries));
93
- newQueries[query.key] = EMPTY_QUERY_STATE;
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 './reducer';
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
- // optimistic response
27
- // type extractors?
28
- // skip -> enabled?
29
- // add full api docs
30
- // update readme with how to use mergeResults for invalidating / updating caches?
31
- // make query key / cache key difference more clear in the docs, and/or rename queryKey -> query?
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.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, MutationResult, Typenames } from './types';
4
- export declare const mutate: <N extends string, T extends Typenames, QP, QR, MP, MR, MK extends keyof MP | keyof MR>(logTag: string, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, { updateMutationStateAndEntities, }: Pick<ActionMap<N, T, unknown, unknown, MP, MR>, "updateMutationStateAndEntities">, mutationKey: MK, params: MK extends keyof MP & keyof MR ? MP[MK] : never, abortControllers: WeakMap<Store, Record<Key, AbortController>>) => Promise<MutationResult<MK extends keyof MP & keyof MR ? MR[MK] : never>>;
4
+ export declare const mutate: <N extends string, T extends Typenames, QP, QR, MP, MR, MK extends keyof MP | keyof MR>(logTag: string, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, { updateMutationStateAndEntities, }: Pick<ActionMap<N, T, unknown, unknown, MP, MR>, "updateMutationStateAndEntities">, mutationKey: MK, params: MK extends keyof MP & keyof MR ? MP[MK] : never, abortControllers: WeakMap<Store, Record<Key, AbortController>>, onCompleted?: ((response: import("./types").QueryResponse<T, MR[keyof MP & keyof MR & string]> | undefined, error: unknown, params: MP[keyof MP & keyof MR & string] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((response: import("./types").QueryResponse<T, MR[keyof MP & keyof MR & number]> | undefined, error: unknown, params: MP[keyof MP & keyof MR & number] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((response: import("./types").QueryResponse<T, MR[keyof MP & keyof MR & symbol]> | undefined, error: unknown, params: MP[keyof MP & keyof MR & symbol] | undefined, store: Store<any, import("redux").AnyAction>) => void) | undefined, onSuccess?: ((response: import("./types").QueryResponse<T, MR[keyof MP & keyof MR & string]>, params: MP[keyof MP & keyof MR & string] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((response: import("./types").QueryResponse<T, MR[keyof MP & keyof MR & number]>, params: MP[keyof MP & keyof MR & number] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((response: import("./types").QueryResponse<T, MR[keyof MP & keyof MR & symbol]>, params: MP[keyof MP & keyof MR & symbol] | undefined, store: Store<any, import("redux").AnyAction>) => void) | undefined, onError?: ((error: unknown, params: MP[keyof MP & keyof MR & string] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((error: unknown, params: MP[keyof MP & keyof MR & number] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((error: unknown, params: MP[keyof MP & keyof MR & symbol] | undefined, store: Store<any, import("redux").AnyAction>) => void) | undefined) => Promise<MutationResult<MK extends keyof MP & keyof MR ? MR[MK] : never>>;
package/dist/mutate.js CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.mutate = void 0;
13
13
  const utilsAndConstants_1 = require("./utilsAndConstants");
14
- const mutate = (logTag, store, cache, { updateMutationStateAndEntities, }, mutationKey, params, abortControllers) => __awaiter(void 0, void 0, void 0, function* () {
14
+ const mutate = (logTag, store, cache, { updateMutationStateAndEntities, }, mutationKey, params, abortControllers, onCompleted = cache.mutations[mutationKey].onCompleted, onSuccess = cache.mutations[mutationKey].onSuccess, onError = cache.mutations[mutationKey].onError) => __awaiter(void 0, void 0, void 0, function* () {
15
15
  let abortControllersOfStore = abortControllers.get(store);
16
16
  if (abortControllersOfStore === undefined) {
17
17
  abortControllersOfStore = {};
@@ -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;
@@ -64,15 +64,23 @@ const mutate = (logTag, store, cache, { updateMutationStateAndEntities, }, mutat
64
64
  error: error,
65
65
  loading: false,
66
66
  }));
67
+ // @ts-expect-error params
68
+ onError === null || onError === void 0 ? void 0 : onError(error, params, store);
69
+ // @ts-expect-error response
70
+ onCompleted === null || onCompleted === void 0 ? void 0 : onCompleted(response, error, params, store);
67
71
  return { error };
68
72
  }
69
73
  if (response) {
70
- store.dispatch(updateMutationStateAndEntities(mutationKey, {
74
+ const newState = {
71
75
  error: undefined,
72
76
  loading: false,
73
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
74
77
  result: response.result,
75
- }, response));
78
+ };
79
+ store.dispatch(updateMutationStateAndEntities(mutationKey, newState, response));
80
+ // @ts-expect-error params
81
+ onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(response, params, store);
82
+ // @ts-expect-error response
83
+ onCompleted === null || onCompleted === void 0 ? void 0 : onCompleted(response, error, params, store);
76
84
  // @ts-expect-error fix later
77
85
  return { result: response.result };
78
86
  }
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, onCompleted?: ((response: import("./types").QueryResponse<T, QR[keyof QP & keyof QR & string]> | undefined, error: unknown, params: QP[keyof QP & keyof QR & string] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((response: import("./types").QueryResponse<T, QR[keyof QP & keyof QR & number]> | undefined, error: unknown, params: QP[keyof QP & keyof QR & number] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((response: import("./types").QueryResponse<T, QR[keyof QP & keyof QR & symbol]> | undefined, error: unknown, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store<any, import("redux").AnyAction>) => void) | undefined, onSuccess?: ((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>) => void) | ((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>) => void) | ((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>) => void) | undefined, onError?: ((error: unknown, params: QP[keyof QP & keyof QR & string] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((error: unknown, params: QP[keyof QP & keyof QR & number] | undefined, store: Store<any, import("redux").AnyAction>) => void) | ((error: unknown, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store<any, import("redux").AnyAction>) => void) | 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, onCompleted = cache.queries[queryKey].onCompleted, onSuccess = cache.queries[queryKey].onSuccess, onError = cache.queries[queryKey].onError) => __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,35 +25,54 @@ 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`, { queryStateOnStart, params, cacheKey });
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, {
43
52
  error: error,
44
53
  loading: false,
45
54
  }));
55
+ // @ts-expect-error params
56
+ onError === null || onError === void 0 ? void 0 : onError(error, params, store);
57
+ // @ts-expect-error params
58
+ onCompleted === null || onCompleted === void 0 ? void 0 : onCompleted(undefined, error, params, store);
46
59
  return { error };
47
60
  }
48
61
  const newState = {
49
62
  error: undefined,
50
63
  loading: false,
64
+ expiresAt: (_a = response.expiresAt) !== null && _a !== void 0 ? _a : (secondsToLive != null ? Date.now() + secondsToLive * 1000 : undefined),
51
65
  result: mergeResults
52
66
  ? mergeResults(
53
67
  // @ts-expect-error fix later
54
- (_a = cacheStateSelector(store.getState()).queries[queryKey][cacheKey]) === null || _a === void 0 ? void 0 : _a.result, response, params, store)
68
+ (_b = cacheStateSelector(store.getState()).queries[queryKey][cacheKey]) === null || _b === void 0 ? void 0 : _b.result, response, params, store)
55
69
  : response.result,
56
70
  };
57
71
  store.dispatch(updateQueryStateAndEntities(queryKey, cacheKey, newState, response));
72
+ // @ts-expect-error response
73
+ onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(response, params, store);
74
+ // @ts-expect-error response
75
+ onCompleted === null || onCompleted === void 0 ? void 0 : onCompleted(response, undefined, params, store);
58
76
  return {
59
77
  // @ts-expect-error fix types
60
78
  result: newState === null || newState === void 0 ? void 0 : newState.result,
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Store } from 'redux';
2
- import type { ReduxCacheState } from './reducer';
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, P, R> = (params: P) => Promise<QueryResponse<T, R>>;
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<T, P, R>;
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
  /**
@@ -85,35 +91,56 @@ export type QueryInfo<T extends Typenames, P, R> = {
85
91
  * 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.
86
92
  * */
87
93
  getCacheKey?: (params?: P) => Key;
94
+ /** Called after fetch finished either successfully or not. */
95
+ onCompleted?: (response: QueryResponse<T, R> | undefined, error: unknown | undefined, params: P | undefined, store: Store) => void;
96
+ /** Called after fetch finished without error. */
97
+ onSuccess?: (response: QueryResponse<T, R>, params: P | undefined, store: Store) => void;
98
+ /** Called after fetch finished with error. */
99
+ onError?: (error: unknown, params: P | undefined, store: Store) => void;
100
+ };
101
+ export type QueryState<P, R> = MutationState<P, R> & {
102
+ expiresAt?: number;
88
103
  };
89
104
  export type UseQueryOptions<T extends Typenames, QP, QR, QK extends keyof (QP & QR)> = {
90
105
  query: QK;
91
106
  params: QK extends keyof (QP | QR) ? QP[QK] : never;
107
+ /** When true fetch is not performed. When switches to false fetch is performed depending on cache policy. */
92
108
  skip?: boolean;
93
- } & Pick<QueryInfo<T, unknown, unknown>, 'cachePolicy'>;
109
+ } & Pick<QueryInfo<T, QK extends keyof (QP | QR) ? QP[QK] : never, QK extends keyof (QP | QR) ? QR[QK] : never>, 'cachePolicy' | 'secondsToLive' | 'mergeResults' | 'onCompleted' | 'onSuccess' | 'onError'>;
110
+ export type QueryOptions<T extends Typenames, QP, QR, QK extends keyof (QP & QR)> = Omit<UseQueryOptions<T, QP, QR, QK>, 'skip' | 'cachePolicy'> & {
111
+ /** If set to true, query will run only if it is expired or result not yet cached. */
112
+ onlyIfExpired?: boolean;
113
+ };
94
114
  /**
95
115
  * @param cache-first for each params key fetch is not called if cache exists.
96
116
  * @param cache-and-fetch for each params key result is taken from cache and fetch is called.
97
117
  */
98
118
  export type QueryCachePolicy = 'cache-first' | 'cache-and-fetch';
99
119
  export type QueryResponse<T extends Typenames, R> = EntityChanges<T> & {
100
- /** Normalized result of a query. */
101
120
  result: R;
121
+ /** If defined, overrides this value for query state, ignoring `secondsToLive`. */
122
+ expiresAt?: number;
102
123
  };
103
124
  export type QueryResult<R> = {
104
125
  error?: unknown;
105
126
  cancelled?: true;
106
127
  result?: R;
107
128
  };
108
- export type QueryOptions<T extends Typenames, QP, QR, QK extends keyof (QP & QR)> = Omit<UseQueryOptions<T, QP, QR, QK>, 'skip'>;
109
- export type Mutation<T extends Typenames, P, R> = (params: P,
129
+ export type Mutation<P, T extends Typenames = Typenames, R = unknown> = (
130
+ /** Mutation parameters */
131
+ params: P,
132
+ /** Redux store */
133
+ store: Store,
110
134
  /** Signal is aborted for current mutation when the same mutation was called once again. */
111
135
  abortSignal: AbortSignal) => Promise<MutationResponse<T, R>>;
112
- export type MutationInfo<T extends Typenames, P, R> = {
113
- mutation: Mutation<T, P, R>;
136
+ export type MutationInfo<T extends Typenames, P, R> = Pick<QueryInfo<T, P, R>, 'onCompleted' | 'onSuccess' | 'onError'> & {
137
+ mutation: Mutation<P, T, R>;
138
+ };
139
+ export type MutateOptions<T extends Typenames, MP, MR, MK extends keyof (MP & MR)> = Pick<MutationInfo<T, MK extends keyof (MP | MR) ? MP[MK] : never, MK extends keyof (MP | MR) ? MR[MK] : never>, 'onCompleted' | 'onSuccess' | 'onError'> & {
140
+ mutation: MK;
141
+ params: MK extends keyof (MP | MR) ? MP[MK] : never;
114
142
  };
115
143
  export type MutationResponse<T extends Typenames, R> = EntityChanges<T> & {
116
- /** Normalized result of a mutation. */
117
144
  result?: R;
118
145
  };
119
146
  export type MutationResult<R> = {
@@ -121,7 +148,7 @@ export type MutationResult<R> = {
121
148
  aborted?: true;
122
149
  result?: R;
123
150
  };
124
- export type QueryMutationState<P, R> = {
151
+ export type MutationState<P, R> = {
125
152
  /** `true` when query or mutation is currently in progress. */
126
153
  loading: boolean;
127
154
  /** Result of the latest successfull response. */
@@ -1,6 +1,4 @@
1
1
  import { Store } from 'redux';
2
2
  import { ActionMap } from './createActions';
3
- import { Cache, Key, QueryMutationState, Typenames } from './types';
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
- 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>>, QueryMutationState<MK extends keyof MP & keyof MR ? MP[MK] : never, MK extends keyof MP & keyof MR ? MP[MK] : never>, () => boolean];
3
+ import { Cache, Key, MutateOptions, MutationState, Typenames } from './types';
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: Omit<MutateOptions<T, MP, MR, MK>, "params">, 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];
@@ -16,7 +16,7 @@ const mutate_1 = require("./mutate");
16
16
  const utilsAndConstants_1 = require("./utilsAndConstants");
17
17
  const useMutation = (cache, actions, options, abortControllers) => {
18
18
  var _a;
19
- const { mutation: mutationKey } = options;
19
+ const { mutation: mutationKey, onCompleted, onSuccess, onError } = options;
20
20
  const store = (0, react_redux_1.useStore)();
21
21
  // Using single useMemo for performance reasons
22
22
  const [mutationStateSelector, mutate, abort] = (0, react_1.useMemo)(() => {
@@ -32,7 +32,9 @@ const useMutation = (cache, actions, options, abortControllers) => {
32
32
  },
33
33
  // mutate
34
34
  (params) => __awaiter(void 0, void 0, void 0, function* () {
35
- return yield (0, mutate_1.mutate)('useMutation.mutate', store, cache, actions, mutationKey, params, abortControllers);
35
+ return yield (0, mutate_1.mutate)('useMutation.mutate', store, cache, actions, mutationKey, params, abortControllers,
36
+ // @ts-expect-error fix later
37
+ onCompleted, onSuccess, onError);
36
38
  }),
37
39
  // abort
38
40
  () => {
@@ -1,5 +1,3 @@
1
1
  import { ActionMap } from './createActions';
2
- import { Cache, QueryMutationState, 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 [QueryMutationState<QK extends keyof QP & keyof QR ? QP[QK] : never, QK extends keyof QP & keyof QR ? QR[QK] : never>, (options?: {
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, onCompleted, onSuccess, onError, } = 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
- options ? getCacheKey(options.params) : cacheKey, options ? options.params : params);
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, onCompleted, onSuccess, onError);
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 && cachePolicy === 'cache-first') {
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 don`t fetch due to cache policy', {
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
- readonly loading: false;
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':
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.6.1",
5
+ "version": "0.7.0-rc.1",
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": {