react-redux-cache 0.22.1 → 0.22.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,7 +46,7 @@ Can be considered as `ApolloClient` for protocols other than `GraphQL`, but with
46
46
  ```js
47
47
  {
48
48
  entities: {
49
- // each typename has its own map of entities, stored by id
49
+ // Each typename has its own map of entities, stored by id.
50
50
  users: {
51
51
  "0": {id: 0, bankId: "0", name: "User 0 *"},
52
52
  "1": {id: 1, bankId: "1", name: "User 1 *"},
@@ -61,13 +61,13 @@ Can be considered as `ApolloClient` for protocols other than `GraphQL`, but with
61
61
  }
62
62
  },
63
63
  queries: {
64
- // each query has its own map of query states, stored by cache key, which is generated from query params
64
+ // Each query has its own map of query states, stored by cache key, which is generated from query params.
65
65
  getUser: {
66
66
  "2": {result: 2, params: 2, expiresAt: 1727217298025},
67
67
  "3": {loading: Promise<...>, params: 3}
68
68
  },
69
69
  getUsers: {
70
- // example of paginated state under custom cache key
70
+ // Example of paginated state under custom cache key.
71
71
  "feed": {
72
72
  result: {items: [0,1,2], page: 1},
73
73
  params: {page: 1}
@@ -192,7 +192,7 @@ export const {
192
192
  reducer,
193
193
  hooks: {useClient, useMutation, useQuery},
194
194
  } = withTypenames<CacheTypenames>().createCache({
195
- name: 'cache', // Used as prefix for actions and in default cacheStateSelector for selecting cache state from redux state.
195
+ name: 'cache', // Used as prefix for actions and in default cacheStateSelector for selecting cache state from the store.
196
196
  queries: {
197
197
  getUsers: { query: getUsers },
198
198
  getUser: {
@@ -273,15 +273,15 @@ export const getUser = (async (id) => {
273
273
  return normalize(response, getUserSchema)
274
274
  }) satisfies NormalizedQuery<CacheTypenames, number>
275
275
 
276
- // Example of query without normalization (not recommended), with selecting access token from the store
276
+ // Example of query without normalization (not recommended), with selecting access token from the store.
277
277
 
278
278
  export const getBank = (async (id, {getState}) => {
279
279
  const token = tokenSelector(getState())
280
280
  const result: Bank = ...
281
- return {result} // result is bank object, no entities passed
281
+ return {result} // result is bank object, no entities passed.
282
282
  }) satisfies Query<string>
283
283
 
284
- // Example of mutation with normalization
284
+ // Example of mutation with normalization.
285
285
 
286
286
  export const removeUser = (async (id, _, abortSignal) => {
287
287
  await ...
@@ -300,18 +300,19 @@ Please check `example/` folder (`npm run example` to run). There are examples fo
300
300
  export const UserScreen = () => {
301
301
  const {id} = useParams()
302
302
 
303
- // useQuery connects to redux state and if user with that id is already cached, fetch won't happen (with default FetchPolicy.NoCacheOrExpired).
304
- // Infers all types from created cache, telling here that params and result are of type `number`.
305
- const [{result: userId, loading, error}] = useQuery({
303
+ // useQuery fetches data if query not cached already (with default FetchPolicy.NoCacheOrExpired).
304
+ // Infers all types from created cache so expects params of type `number`.
305
+ const [{result: user, loading, error}] = useQuery({
306
306
  query: 'getUser',
307
307
  params: Number(id),
308
308
  })
309
309
 
310
+ // Globally tracks loading state for mutation.
310
311
  const [updateUser, {loading: updatingUser}] = useMutation({
311
312
  mutation: 'updateUser',
312
313
  })
313
314
 
314
- // This selector returns entities with proper types - User and Bank
315
+ // This selector used only with normalization and returns entities with proper types - User and Bank.
315
316
  const user = useSelectEntityById(userId, 'users')
316
317
  const bank = useSelectEntityById(user?.bankId, 'banks')
317
318
 
@@ -329,12 +330,12 @@ export const UserScreen = () => {
329
330
 
330
331
  For huge collections (> 1000 items, see benchmark) immutable approach may be a bottleneck - every merge of entity, query or mutation state is O(n). There is an option `mutableCollections` that makes it O(1) by using mutable approach when working with collections, while still keeping separate entities, query and mutation states immutable.
331
332
 
332
- [Benchmark](https://github.com/gentlee/react-redux-cache/blob/main/benchmark.ts) results of adding item to collection depending on collection size, in microseconds (less is better):
333
+ [Benchmark](https://github.com/gentlee/react-redux-cache/blob/main/scripts/benchmark.mjs) results of adding item to collection depending on collection size, in microseconds (Macbook M1, less is better):
333
334
 
334
335
  | Collection size | 0 | 1000 | 10000 | 100000 | 1000000 |
335
336
  |-|-|-|-|-|-|
336
- | immutable | 1.78 | 1.69 | 5.64 | 117.4 | 1284.6 |
337
- | mutable | 1.68 | 0.92 | 0.98 | 0.98 | 0.99 |
337
+ | immutable | 1.57 | 1.81 | 7.62 | 103.82 | 1457.89 |
338
+ | mutable | 1.4 | 1.15 | 0.65 | 1.03 | 0.76 |
338
339
 
339
340
  Well written code should not subcribe to whole collections, so just enabling this options most of the times should not break anything. But if it is still needed, you should subscribe to both collection (it may still change e.g. when clearing state) and to its `_changeKey`.
340
341
 
@@ -342,7 +343,7 @@ Well written code should not subcribe to whole collections, so just enabling thi
342
343
  const Component = () => {
343
344
  // It is usually a bad idea to subscribe to whole collections, consider using order of ids and subscribe to a single entity in each cell.
344
345
  const allUsers = useSelector((state) => selectEntitiesByTypename(state, 'users'))
345
- const allUsersChangeKey = useSelector((state) => selectEntitiesByTypename(state, 'users')._changeKey) // <-- Add this line while subscribing to collections
346
+ const allUsersChangeKey = useSelector((state) => selectEntitiesByTypename(state, 'users')._changeKey) // <-- Add this line while subscribing to collections.
346
347
 
347
348
  // For memoized components you should also pass it as extra prop to cause its re-render.
348
349
  return <List data={allUsers} extra={allUsersChangeKey}/>
@@ -366,8 +367,8 @@ export const updateBank = (async (bank) => {
366
367
  const {httpError, response} = ...
367
368
  return {
368
369
  result: {
369
- httpError, // Error is a part of the result, containing e.g. map of not valid fields and threir error messages
370
- bank: response?.bank // Bank still can be returned from the backend with error e.g. when only some of fields were udpated
370
+ httpError, // Error is a part of the result, containing e.g. map of not valid fields and threir error messages.
371
+ bank: response?.bank // Bank still can be returned from the backend with error e.g. when only some of fields were udpated.
371
372
  }
372
373
  }
373
374
  }) satisfies Mutation<Partial<Bank>>
@@ -402,7 +403,7 @@ export const cache = createCache({
402
403
  updateUser: {
403
404
  mutation: updateUser,
404
405
  onSuccess(_, __, {dispatch}, {invalidateQuery}) {
405
- // Invalidate getUsers after a single user update (can be done better by updating getUsers state with updateQueryStateAndEntities)
406
+ // Invalidate getUsers after a single user update (can be done better by updating getUsers state with updateQueryStateAndEntities).
406
407
  dispatch(invalidateQuery([{query: 'getUsers'}]))
407
408
  },
408
409
  },
@@ -425,7 +426,7 @@ export const UserScreen = () => {
425
426
  const [{loading, error}] = useQuery({
426
427
  query: 'getUser',
427
428
  params: userId,
428
- skipFetch: !!user // Disable fetches if we already have user cached by some other query, e.g. getUsers
429
+ skipFetch: !!user // Disable fetches if we already have user cached by some other query, e.g. getUsers.
429
430
  })
430
431
 
431
432
  ...
@@ -440,10 +441,10 @@ But if more control is needed, e.g. checking if entity is full, custom fetch pol
440
441
  query: getUser,
441
442
  fetchPolicy(expired, id, _, {getState}, {selectEntityById}) {
442
443
  if (expired) {
443
- return true // fetch if expired
444
+ return true // Fetch if expired.
444
445
  }
445
446
 
446
- // fetch if user is not full
447
+ // Fetch if user is not full.
447
448
  const user = selectEntityById(getState(), id, 'users')
448
449
  return !user || !('name' in user) || !('bankId' in user)
449
450
  },
@@ -465,7 +466,9 @@ export const UserScreen = () => {
465
466
 
466
467
  useEffect(() => {
467
468
  if (screenIsVisible) {
468
- 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.
469
+ // Expiration happens if expiresAt was set before e.g. by secondsToLive option or invalidateQuery action.
470
+ // If result is not cached yet, it is also considered as expired.
471
+ fetchUser({ onlyIfExpired: true })
469
472
  }
470
473
  }, [screenIsVisible])
471
474
 
@@ -486,7 +489,7 @@ Here is an example of `getUsers` query configuration with pagination support. Yo
486
489
  queries: {
487
490
  getUsers: {
488
491
  query: getUsers,
489
- getCacheKey: () => 'feed', // single cache key is used for all pages
492
+ getCacheKey: () => 'feed', // Single cache key is used for all pages.
490
493
  mergeResults: (oldResult, {result: newResult}) => {
491
494
  if (!oldResult || newResult.page === 1) {
492
495
  return newResult
@@ -549,16 +552,17 @@ export const GetUsersScreen = () => {
549
552
  Here is a simple `redux-persist` configuration:
550
553
 
551
554
  ```typescript
552
- // removes `loading` and `error` from persisted state
555
+ // Removes `loading` and `error` from persisted state.
556
+ // '_changeKey' is needed only when `mutuableCollections` enabled.
553
557
  function stringifyReplacer(key: string, value: unknown) {
554
- return key === 'loading' || key === 'error' ? undefined : value
558
+ return key === 'loading' || key === 'error' || key === '_changeKey' ? undefined : value
555
559
  }
556
560
 
557
561
  const persistedReducer = persistReducer(
558
562
  {
559
563
  key: 'cache',
560
564
  storage,
561
- whitelist: ['entities', 'queries'], // mutations are ignored
565
+ whitelist: ['entities', 'queries'], // Mutation states are ignored.
562
566
  throttle: 1000, // ms
563
567
  serialize: (value: unknown) => JSON.stringify(value, stringifyReplacer),
564
568
  },
@@ -593,6 +597,6 @@ As example, can be overridden when implementing pagination.
593
597
 
594
598
  #### How race conditions are handled?
595
599
 
596
- **Queries:** Queries are throttled: query with the same cache key (generated from params by default) is cancelled if already running.
600
+ **Queries:** Queries are deduplicated: queries with the same cache key (generated from params by default) use existing fetch promise if already fetching.
597
601
 
598
- **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.
602
+ **Mutations:** Mutations are cancelled: 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,64 +1,64 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createActions = void 0;
4
- const utilsAndConstants_1 = require("./utilsAndConstants");
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', {value: true})
3
+ exports.createActions = void 0
4
+ const utilsAndConstants_1 = require('./utilsAndConstants')
5
5
  const createActions = (name) => {
6
- const actionPrefix = `@${utilsAndConstants_1.PACKAGE_SHORT_NAME}/${name}/`;
7
- const updateQueryStateAndEntitiesType = `${actionPrefix}updateQueryStateAndEntities`;
8
- const updateQueryStateAndEntities = (queryKey, queryCacheKey, state, entityChanges) => ({
9
- type: updateQueryStateAndEntitiesType,
10
- queryKey,
11
- queryCacheKey,
12
- state,
13
- entityChanges,
14
- });
15
- updateQueryStateAndEntities.type = updateQueryStateAndEntitiesType;
16
- const updateMutationStateAndEntitiesType = `${actionPrefix}updateMutationStateAndEntities`;
17
- const updateMutationStateAndEntities = (mutationKey, state, entityChanges) => ({
18
- type: updateMutationStateAndEntitiesType,
19
- mutationKey,
20
- state,
21
- entityChanges,
22
- });
23
- updateMutationStateAndEntities.type = updateMutationStateAndEntitiesType;
24
- const mergeEntityChangesType = `${actionPrefix}mergeEntityChanges`;
25
- const mergeEntityChanges = (changes) => ({
26
- type: mergeEntityChangesType,
27
- changes,
28
- });
29
- mergeEntityChanges.type = mergeEntityChangesType;
30
- const invalidateQueryType = `${actionPrefix}invalidateQuery`;
31
- const invalidateQuery = (queries) => ({
32
- type: invalidateQueryType,
33
- queries,
34
- });
35
- invalidateQuery.type = invalidateQueryType;
36
- const clearQueryStateType = `${actionPrefix}clearQueryState`;
37
- const clearQueryState = (queries) => ({
38
- type: clearQueryStateType,
39
- queries,
40
- });
41
- clearQueryState.type = clearQueryStateType;
42
- const clearMutationStateType = `${actionPrefix}clearMutationState`;
43
- const clearMutationState = (mutationKeys) => ({
44
- type: clearMutationStateType,
45
- mutationKeys,
46
- });
47
- clearMutationState.type = clearMutationStateType;
48
- const clearCacheType = `${actionPrefix}clearCache`;
49
- const clearCache = (stateToKeep) => ({
50
- type: clearCacheType,
51
- stateToKeep,
52
- });
53
- clearCache.type = clearCacheType;
54
- return {
55
- updateQueryStateAndEntities,
56
- updateMutationStateAndEntities,
57
- mergeEntityChanges,
58
- invalidateQuery,
59
- clearQueryState,
60
- clearMutationState,
61
- clearCache,
62
- };
63
- };
64
- exports.createActions = createActions;
6
+ const actionPrefix = `@${utilsAndConstants_1.PACKAGE_SHORT_NAME}/${name}/`
7
+ const updateQueryStateAndEntitiesType = `${actionPrefix}updateQueryStateAndEntities`
8
+ const updateQueryStateAndEntities = (queryKey, queryCacheKey, state, entityChanges) => ({
9
+ type: updateQueryStateAndEntitiesType,
10
+ queryKey,
11
+ queryCacheKey,
12
+ state,
13
+ entityChanges,
14
+ })
15
+ updateQueryStateAndEntities.type = updateQueryStateAndEntitiesType
16
+ const updateMutationStateAndEntitiesType = `${actionPrefix}updateMutationStateAndEntities`
17
+ const updateMutationStateAndEntities = (mutationKey, state, entityChanges) => ({
18
+ type: updateMutationStateAndEntitiesType,
19
+ mutationKey,
20
+ state,
21
+ entityChanges,
22
+ })
23
+ updateMutationStateAndEntities.type = updateMutationStateAndEntitiesType
24
+ const mergeEntityChangesType = `${actionPrefix}mergeEntityChanges`
25
+ const mergeEntityChanges = (changes) => ({
26
+ type: mergeEntityChangesType,
27
+ changes,
28
+ })
29
+ mergeEntityChanges.type = mergeEntityChangesType
30
+ const invalidateQueryType = `${actionPrefix}invalidateQuery`
31
+ const invalidateQuery = (queries) => ({
32
+ type: invalidateQueryType,
33
+ queries,
34
+ })
35
+ invalidateQuery.type = invalidateQueryType
36
+ const clearQueryStateType = `${actionPrefix}clearQueryState`
37
+ const clearQueryState = (queries) => ({
38
+ type: clearQueryStateType,
39
+ queries,
40
+ })
41
+ clearQueryState.type = clearQueryStateType
42
+ const clearMutationStateType = `${actionPrefix}clearMutationState`
43
+ const clearMutationState = (mutationKeys) => ({
44
+ type: clearMutationStateType,
45
+ mutationKeys,
46
+ })
47
+ clearMutationState.type = clearMutationStateType
48
+ const clearCacheType = `${actionPrefix}clearCache`
49
+ const clearCache = (stateToKeep) => ({
50
+ type: clearCacheType,
51
+ stateToKeep,
52
+ })
53
+ clearCache.type = clearCacheType
54
+ return {
55
+ updateQueryStateAndEntities,
56
+ updateMutationStateAndEntities,
57
+ mergeEntityChanges,
58
+ invalidateQuery,
59
+ clearQueryState,
60
+ clearMutationState,
61
+ clearCache,
62
+ }
63
+ }
64
+ exports.createActions = createActions