react-redux-cache 0.14.0 → 0.16.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 CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  # react-redux-cache (RRC)
11
11
 
12
- **Powerful** yet **lightweight** data fetching and caching library that supports **normalization** unlike `React Query` and `RTK-Query`, while having similar but not over-engineered, simple interface. Another advantage over `RTK-Query` is that it easily supports `Infinite Scroll`. Built on top of `Redux`, covered with tests, fully typed and written on Typescript.
12
+ **Powerful**, **performant** yet **lightweight** data fetching and caching library that supports **normalization** unlike `React Query` and `RTK-Query`, while having similar but not over-engineered, simple interface. Another advantage over `RTK-Query` is that it **doesn't use Immer** ([perf issue](https://github.com/reduxjs/redux-toolkit/issues/4793)). Built on top of `Redux`, covered with tests, fully typed and written on Typescript.
13
13
 
14
14
  **Normalization** is the best way to keep the state of the app **consistent** between different views, reduces the number of fetches and allows to show cached data when navigating, which greatly improves **user experience**.
15
15
 
@@ -127,11 +127,21 @@ Examples of states, generated by cache reducer from `/example` project:
127
127
  - [How race conditions are handled?](https://github.com/gentlee/react-redux-cache#how-race-conditions-are-handled)
128
128
 
129
129
  ### Installation
130
- `react`, `redux` and `react-redux` are peer dependencies.
130
+ `react` is a peer dependency.
131
+
132
+ `react-redux` and `fast-deep-equal` are optional peer dependencies:
133
+ - `react-redux` required when `storeHooks` is not provided when creating cache.
134
+ - `fast-deep-equal` required if `deepComparisonEnabled` cache option is enabled (default is true).
131
135
 
132
- `fast-deep-equal` is an optional peer dependency if `deepComparisonEnabled` cache option is enabled (default is true).
133
136
  ```sh
134
- npm add react-redux-cache react redux react-redux fast-deep-equal
137
+ # required
138
+ npm add react-redux-cache react
139
+
140
+ # without react-redux
141
+ npm add react-redux-cache react fast-deep-equal
142
+
143
+ # all required and optional peers
144
+ npm add react-redux-cache react react-redux fast-deep-equal
135
145
  ```
136
146
  ### Initialization
137
147
  The only function that needs to be imported is either `withTypenames`, which is needed for normalization, or directly `createCache` if it is not needed. `createCache` 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.
@@ -145,15 +155,14 @@ export type CacheTypenames = {
145
155
  banks: Bank,
146
156
  }
147
157
 
158
+ // `withTypenames` is only needed to provide proper Typenames for normalization - limitation of Typescript.
159
+ // `createCache` can be imported directly without `withTypenames`.
148
160
  export const {
149
161
  cache,
150
162
  reducer,
151
163
  hooks: {useClient, useMutation, useQuery},
152
- // `withTypenames` is only needed to provide proper Typenames for normalization - limitation of Typescript.
153
- // `createCache` can be imported directly without `withTypenames`.
154
164
  } = withTypenames<CacheTypenames>().createCache({
155
- // Used as prefix for actions and in default cacheStateSelector for selecting cache state from redux state.
156
- name: 'cache',
165
+ name: 'cache', // Used as prefix for actions and in default cacheStateSelector for selecting cache state from redux state.
157
166
  queries: {
158
167
  getUsers: { query: getUsers },
159
168
  getUser: {
@@ -176,15 +185,11 @@ For normalization two things are required:
176
185
  - Return an object from queries and mutations that contains the following fields (besides `result`):
177
186
 
178
187
  ```typescript
179
- type EntityChanges<T extends Typenames> = {
180
- /** Entities that will be merged with existing. */
181
- merge?: PartialEntitiesMap<T>
182
- /** Entities that will replace existing. */
183
- replace?: Partial<EntitiesMap<T>>
184
- /** Ids of entities that will be removed. */
185
- remove?: EntityIds<T>
186
- /** Alias for `merge` to support normalizr. */
187
- entities?: EntityChanges<T>['merge']
188
+ type EntityChanges<T extends Typenames> = {
189
+ merge?: PartialEntitiesMap<T> /** Entities that will be merged with existing. */
190
+ replace?: Partial<EntitiesMap<T>> /** Entities that will replace existing. */
191
+ remove?: EntityIds<T> /** Ids of entities that will be removed. */
192
+ entities?: EntityChanges<T>['merge'] /** Alias for `merge` to support normalizr. */
188
193
  }
189
194
  ```
190
195
 
@@ -206,14 +211,12 @@ Perfect implementation is when the backend already returns normalized data.
206
211
 
207
212
  // Example of query with normalization (recommended)
208
213
 
214
+ // 1. Result can be get by any way - fetch, axios etc, even with database connection. There is no limitation here.
215
+ // 2. `satisfies` keyword is used here for proper typing of params and returned value.
209
216
  export const getUser = (async (id) => {
210
- // Result can be get by any way - fetch, axios etc, even with database connection.
211
- // There is no limitation here.
212
217
  const response = await ...
213
218
 
214
- // In this example normalizr package is used, but it is not necessary.
215
219
  return normalize(response, getUserSchema)
216
- // satisfies keyword is used here for proper typing of params and returned value.
217
220
  }) satisfies NormalizedQuery<CacheTypenames, number>
218
221
 
219
222
  // Example of query without normalization (not recommended), with selecting access token from the store
@@ -277,10 +280,8 @@ export const updateBank = (async (bank) => {
277
280
  const {httpError, response} = ...
278
281
  return {
279
282
  result: {
280
- // Error is a part of the result, containing e.g. map of not valid fields and threir error messages
281
- httpError,
282
- // Bank still can be returned from the backend with error e.g. when only some of fields were udpated
283
- bank: response?.bank
283
+ httpError, // Error is a part of the result, containing e.g. map of not valid fields and threir error messages
284
+ bank: response?.bank // Bank still can be returned from the backend with error e.g. when only some of fields were udpated
284
285
  }
285
286
  }
286
287
  }) satisfies Mutation<Partial<Bank>>
@@ -502,7 +503,7 @@ export const defaultGetCacheKey = <P = unknown>(params: P): Key => {
502
503
 
503
504
  It is recommended to override it when default implementation is not optimal or when keys in params object can be sorted in random order.
504
505
 
505
- As example, can be overriden when implementing pagination.
506
+ As example, can be overridden when implementing pagination.
506
507
 
507
508
  #### How race conditions are handled?
508
509
 
@@ -1,5 +1,5 @@
1
1
  import type { EntityChanges, Key, MutationState, QueryState, Typenames } from './types';
2
- import { ReduxCacheState } from './types';
2
+ import { CacheState } from './types';
3
3
  export type Actions<N extends string = string, T extends Typenames = Typenames, QP = unknown, QR = unknown, MP = unknown, MR = unknown> = ReturnType<typeof createActions<N, T, QP, QR, MP, MR>>;
4
4
  export declare const createActions: <N extends string, T extends Typenames, QP, QR, MP, MR>(name: N) => {
5
5
  updateQueryStateAndEntities: {
@@ -74,9 +74,9 @@ export declare const createActions: <N extends string, T extends Typenames, QP,
74
74
  type: `@rrc/${N}/clearMutationState`;
75
75
  };
76
76
  clearCache: {
77
- (stateToKeep?: Partial<ReduxCacheState<T, QP, QR, MP, MR>>): {
77
+ (stateToKeep?: Partial<CacheState<T, QP, QR, MP, MR>>): {
78
78
  type: `@rrc/${N}/clearCache`;
79
- stateToKeep: Partial<ReduxCacheState<T, QP, QR, MP, MR>> | undefined;
79
+ stateToKeep: Partial<CacheState<T, QP, QR, MP, MR>> | undefined;
80
80
  };
81
81
  type: `@rrc/${N}/clearCache`;
82
82
  };
@@ -1,4 +1,4 @@
1
- import type { Cache, CacheOptions, Globals, Key, MutateOptions, MutationResult, OptionalPartial, QueryOptions, QueryResult, Typenames } from './types';
1
+ import type { Cache, CacheOptions, Globals, Key, MutateOptions, MutationResult, OptionalPartial, QueryOptions, QueryResult, Store, Typenames } from './types';
2
2
  import { useMutation } from './useMutation';
3
3
  import { useQuery } from './useQuery';
4
4
  import { applyEntityChanges } from './utilsAndConstants';
@@ -11,13 +11,13 @@ import { applyEntityChanges } from './utilsAndConstants';
11
11
  * })
12
12
  */
13
13
  export declare const withTypenames: <T extends Typenames = Typenames>() => {
14
- createCache: <N extends string, QP, QR, MP, MR>(partialCache: OptionalPartial<Omit<Cache<N, T, QP, QR, MP, MR>, "globals">, "options" | "queries" | "mutations" | "cacheStateSelector"> & {
14
+ createCache: <N extends string, QP, QR, MP, MR>(partialCache: OptionalPartial<Omit<Cache<N, T, QP, QR, MP, MR>, "globals">, "options" | "queries" | "mutations" | "cacheStateSelector" | "storeHooks"> & {
15
15
  globals?: OptionalPartial<Cache<N, T, QP, QR, MP, MR>["globals"], "queries">;
16
16
  }) => {
17
17
  /** Keeps all options, passed while creating the cache. */
18
18
  cache: Cache<N, T, QP, QR, MP, MR>;
19
19
  /** Reducer of the cache, should be added to redux store. */
20
- reducer: (state: import("./types").ReduxCacheState<T, QP, QR, MP, MR> | undefined, action: {
20
+ reducer: (state: import("./types").CacheState<T, QP, QR, MP, MR> | undefined, action: {
21
21
  type: `@rrc/${N}/updateQueryStateAndEntities`;
22
22
  queryKey: keyof QP & keyof QR;
23
23
  queryCacheKey: Key;
@@ -49,8 +49,8 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
49
49
  mutationKeys: (keyof MP & keyof MR)[];
50
50
  } | {
51
51
  type: `@rrc/${N}/clearCache`;
52
- stateToKeep: Partial<import("./types").ReduxCacheState<T, QP, QR, MP, MR>> | undefined;
53
- }) => import("./types").ReduxCacheState<T, QP, QR, MP, MR>;
52
+ stateToKeep: Partial<import("./types").CacheState<T, QP, QR, MP, MR>> | undefined;
53
+ }) => import("./types").CacheState<T, QP, QR, MP, MR>;
54
54
  actions: {
55
55
  /** Updates query state, and optionally merges entity changes in a single action. */
56
56
  updateQueryStateAndEntities: {
@@ -122,16 +122,16 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
122
122
  };
123
123
  /** Replaces cache state with initial, optionally merging with provided state. Doesn't cancel running fetches and shoult be used with caution. */
124
124
  clearCache: {
125
- (stateToKeep?: Partial<import("./types").ReduxCacheState<T, QP, QR, MP, MR>> | undefined): {
125
+ (stateToKeep?: Partial<import("./types").CacheState<T, QP, QR, MP, MR>> | undefined): {
126
126
  type: `@rrc/${N}/clearCache`;
127
- stateToKeep: Partial<import("./types").ReduxCacheState<T, QP, QR, MP, MR>> | undefined;
127
+ stateToKeep: Partial<import("./types").CacheState<T, QP, QR, MP, MR>> | undefined;
128
128
  };
129
129
  type: `@rrc/${N}/clearCache`;
130
130
  };
131
131
  };
132
132
  selectors: {
133
133
  /** This is a cacheStateSelector from createCache options, or default one if was not provided. */
134
- selectCacheState: (state: any) => import("./types").ReduxCacheState<T, QP, QR, MP, MR>;
134
+ selectCacheState: (state: any) => import("./types").CacheState<T, QP, QR, MP, MR>;
135
135
  /** Selects query state. */
136
136
  selectQueryState: <QK extends keyof QP | keyof QR>(state: unknown, query: QK, cacheKey: Key) => import("./types").QueryState<QK extends keyof QP & keyof QR ? QP[QK] : never, QK extends keyof QP & keyof QR ? QR[QK] : never>;
137
137
  /** Selects query latest result. */
@@ -188,14 +188,18 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(partialCach
188
188
  queries: Partial<{ [QK in keyof (QP & QR)]: QK extends keyof QP & keyof QR ? import("./types").QueryInfo<N, Typenames, QP[QK], QR[QK], QP, QR, MP, MR> : never; }>;
189
189
  mutations: Partial<{ [MK in keyof (MP & MR)]: MK extends keyof MP & keyof MR ? import("./types").MutationInfo<N, Typenames, MP[MK], MR[MK], QP, QR, MP, MR> : never; }>;
190
190
  options: Partial<CacheOptions>;
191
- cacheStateSelector: Partial<(state: any) => import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR>>;
192
- }> & Omit<Omit<Cache<N, Typenames, QP, QR, MP, MR>, "globals">, "queries" | "mutations" | "options" | "cacheStateSelector"> & {
191
+ storeHooks: Partial<{
192
+ useStore: () => Store;
193
+ useSelector: <R>(selector: (state: unknown) => R, comparer?: (x: R, y: R) => boolean) => R;
194
+ }>;
195
+ cacheStateSelector: Partial<(state: any) => import("./types").CacheState<Typenames, QP, QR, MP, MR>>;
196
+ }> & Omit<Omit<Cache<N, Typenames, QP, QR, MP, MR>, "globals">, "queries" | "mutations" | "options" | "storeHooks" | "cacheStateSelector"> & {
193
197
  globals?: OptionalPartial<Globals<N, Typenames, QP, QR, MP, MR>, "queries"> | undefined;
194
198
  }) => {
195
199
  /** Keeps all options, passed while creating the cache. */
196
200
  cache: Cache<N, Typenames, QP, QR, MP, MR>;
197
201
  /** Reducer of the cache, should be added to redux store. */
198
- reducer: (state: import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR> | undefined, action: {
202
+ reducer: (state: import("./types").CacheState<Typenames, QP, QR, MP, MR> | undefined, action: {
199
203
  type: `@rrc/${N}/updateQueryStateAndEntities`;
200
204
  queryKey: keyof QP & keyof QR;
201
205
  queryCacheKey: Key;
@@ -227,8 +231,8 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(partialCach
227
231
  mutationKeys: (keyof MP & keyof MR)[];
228
232
  } | {
229
233
  type: `@rrc/${N}/clearCache`;
230
- stateToKeep: Partial<import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR>> | undefined;
231
- }) => import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR>;
234
+ stateToKeep: Partial<import("./types").CacheState<Typenames, QP, QR, MP, MR>> | undefined;
235
+ }) => import("./types").CacheState<Typenames, QP, QR, MP, MR>;
232
236
  actions: {
233
237
  /** Updates query state, and optionally merges entity changes in a single action. */
234
238
  updateQueryStateAndEntities: {
@@ -300,16 +304,16 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(partialCach
300
304
  };
301
305
  /** Replaces cache state with initial, optionally merging with provided state. Doesn't cancel running fetches and shoult be used with caution. */
302
306
  clearCache: {
303
- (stateToKeep?: Partial<import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR>> | undefined): {
307
+ (stateToKeep?: Partial<import("./types").CacheState<Typenames, QP, QR, MP, MR>> | undefined): {
304
308
  type: `@rrc/${N}/clearCache`;
305
- stateToKeep: Partial<import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR>> | undefined;
309
+ stateToKeep: Partial<import("./types").CacheState<Typenames, QP, QR, MP, MR>> | undefined;
306
310
  };
307
311
  type: `@rrc/${N}/clearCache`;
308
312
  };
309
313
  };
310
314
  selectors: {
311
315
  /** This is a cacheStateSelector from createCache options, or default one if was not provided. */
312
- selectCacheState: (state: any) => import("./types").ReduxCacheState<Typenames, QP, QR, MP, MR>;
316
+ selectCacheState: (state: any) => import("./types").CacheState<Typenames, QP, QR, MP, MR>;
313
317
  /** Selects query state. */
314
318
  selectQueryState: <QK_1 extends keyof QP | keyof QR>(state: unknown, query: QK_1, cacheKey: Key) => import("./types").QueryState<QK_1 extends keyof QP & keyof QR ? QP[QK_1] : never, QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never>;
315
319
  /** Selects query latest result. */
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createCache = exports.withTypenames = void 0;
4
4
  const react_1 = require("react");
5
- const react_redux_1 = require("react-redux");
6
5
  const createActions_1 = require("./createActions");
7
6
  const createCacheReducer_1 = require("./createCacheReducer");
8
7
  const createSelectors_1 = require("./createSelectors");
@@ -25,22 +24,26 @@ const withTypenames = () => {
25
24
  */
26
25
  return {
27
26
  createCache: (partialCache) => {
28
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
29
- var _m, _o, _p, _q, _r, _s;
27
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
28
+ var _q, _r, _s, _t, _u, _v, _w, _x;
30
29
  const abortControllers = new WeakMap();
31
30
  // provide all optional fields
32
31
  (_a = partialCache.options) !== null && _a !== void 0 ? _a : (partialCache.options = {});
33
- (_b = (_m = partialCache.options).logsEnabled) !== null && _b !== void 0 ? _b : (_m.logsEnabled = false);
34
- (_c = (_o = partialCache.options).additionalValidation) !== null && _c !== void 0 ? _c : (_o.additionalValidation = utilsAndConstants_1.IS_DEV);
35
- (_d = (_p = partialCache.options).deepComparisonEnabled) !== null && _d !== void 0 ? _d : (_p.deepComparisonEnabled = true);
36
- (_e = partialCache.queries) !== null && _e !== void 0 ? _e : (partialCache.queries = {});
37
- (_f = partialCache.mutations) !== null && _f !== void 0 ? _f : (partialCache.mutations = {});
38
- (_g = partialCache.globals) !== null && _g !== void 0 ? _g : (partialCache.globals = {});
39
- (_h = (_q = partialCache.globals).queries) !== null && _h !== void 0 ? _h : (_q.queries = {});
40
- (_j = (_r = partialCache.globals.queries).fetchPolicy) !== null && _j !== void 0 ? _j : (_r.fetchPolicy = utilsAndConstants_1.FetchPolicy.NoCacheOrExpired);
41
- (_k = (_s = partialCache.globals.queries).skipFetch) !== null && _k !== void 0 ? _k : (_s.skipFetch = false);
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- (_l = partialCache.cacheStateSelector) !== null && _l !== void 0 ? _l : (partialCache.cacheStateSelector = (state) => state[cache.name]);
32
+ (_b = (_q = partialCache.options).logsEnabled) !== null && _b !== void 0 ? _b : (_q.logsEnabled = false);
33
+ (_c = (_r = partialCache.options).additionalValidation) !== null && _c !== void 0 ? _c : (_r.additionalValidation = utilsAndConstants_1.IS_DEV);
34
+ (_d = (_s = partialCache.options).deepComparisonEnabled) !== null && _d !== void 0 ? _d : (_s.deepComparisonEnabled = true);
35
+ (_e = partialCache.globals) !== null && _e !== void 0 ? _e : (partialCache.globals = {});
36
+ (_f = (_t = partialCache.globals).queries) !== null && _f !== void 0 ? _f : (_t.queries = {});
37
+ (_g = (_u = partialCache.globals.queries).fetchPolicy) !== null && _g !== void 0 ? _g : (_u.fetchPolicy = utilsAndConstants_1.FetchPolicy.NoCacheOrExpired);
38
+ (_h = (_v = partialCache.globals.queries).skipFetch) !== null && _h !== void 0 ? _h : (_v.skipFetch = false);
39
+ (_j = partialCache.storeHooks) !== null && _j !== void 0 ? _j : (partialCache.storeHooks = {});
40
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
41
+ (_k = (_w = partialCache.storeHooks).useStore) !== null && _k !== void 0 ? _k : (_w.useStore = require('react-redux').useStore);
42
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
43
+ (_l = (_x = partialCache.storeHooks).useSelector) !== null && _l !== void 0 ? _l : (_x.useSelector = require('react-redux').useSelector);
44
+ (_m = partialCache.cacheStateSelector) !== null && _m !== void 0 ? _m : (partialCache.cacheStateSelector = (state) => state[cache.name]);
45
+ (_o = partialCache.mutations) !== null && _o !== void 0 ? _o : (partialCache.mutations = {});
46
+ (_p = partialCache.queries) !== null && _p !== void 0 ? _p : (partialCache.queries = {});
44
47
  // @ts-expect-error private field for testing
45
48
  partialCache.abortControllers = abortControllers;
46
49
  const cache = partialCache;
@@ -49,8 +52,8 @@ const withTypenames = () => {
49
52
  console.warn('react-redux-cache: optional dependency for fast-deep-equal was not provided, while deepComparisonEnabled option is true');
50
53
  }
51
54
  // selectors
52
- const selectors = (0, createSelectors_1.createSelectors)(cache);
53
- const { selectQueryState, selectQueryResult, selectQueryLoading, selectQueryError, selectQueryParams, selectQueryExpiresAt, selectMutationState, selectMutationResult, selectMutationLoading, selectMutationError, selectMutationParams, selectEntityById, selectEntities, selectEntitiesByTypename, } = selectors;
55
+ const selectors = Object.assign({ selectCacheState: cache.cacheStateSelector }, (0, createSelectors_1.createSelectors)(cache));
56
+ const { selectCacheState, selectQueryState, selectQueryResult, selectQueryLoading, selectQueryError, selectQueryParams, selectQueryExpiresAt, selectMutationState, selectMutationResult, selectMutationLoading, selectMutationError, selectMutationParams, selectEntityById, selectEntities, selectEntitiesByTypename, } = selectors;
54
57
  // actions
55
58
  const actions = (0, createActions_1.createActions)(cache.name);
56
59
  const { updateQueryStateAndEntities, updateMutationStateAndEntities, mergeEntityChanges, invalidateQuery, clearQueryState, clearMutationState, clearCache, } = actions;
@@ -78,7 +81,7 @@ const withTypenames = () => {
78
81
  },
79
82
  selectors: {
80
83
  /** This is a cacheStateSelector from createCache options, or default one if was not provided. */
81
- selectCacheState: cache.cacheStateSelector,
84
+ selectCacheState,
82
85
  /** Selects query state. */
83
86
  selectQueryState,
84
87
  /** Selects query latest result. */
@@ -111,7 +114,7 @@ const withTypenames = () => {
111
114
  hooks: {
112
115
  /** Returns client object with query and mutate functions. */
113
116
  useClient: () => {
114
- const store = (0, react_redux_1.useStore)();
117
+ const store = cache.storeHooks.useStore();
115
118
  return (0, react_1.useMemo)(() => {
116
119
  const client = {
117
120
  query: (options) => {
@@ -139,7 +142,7 @@ const withTypenames = () => {
139
142
  useMutation: (options) => (0, useMutation_1.useMutation)(cache, actions, selectors, options, abortControllers),
140
143
  /** useSelector + selectEntityById. */
141
144
  useSelectEntityById: (id, typename) => {
142
- return (0, react_redux_1.useSelector)((state) => selectEntityById(state, id, typename));
145
+ return cache.storeHooks.useSelector((state) => selectEntityById(state, id, typename));
143
146
  },
144
147
  },
145
148
  utils: {
@@ -1,3 +1,3 @@
1
1
  import type { Actions } from './createActions';
2
- import type { CacheOptions, ReduxCacheState, Typenames } from './types';
3
- export declare const createCacheReducer: <N extends string, T extends Typenames, QP, QR, MP, MR>(actions: Actions<N, T, QP, QR, MP, MR>, queryKeys: (keyof (QP | QR))[], cacheOptions: CacheOptions) => (state: ReduxCacheState<T, QP, QR, MP, MR> | undefined, action: ReturnType<(typeof actions)[keyof typeof actions]>) => ReduxCacheState<T, QP, QR, MP, MR>;
2
+ import type { CacheOptions, CacheState, Typenames } from './types';
3
+ export declare const createCacheReducer: <N extends string, T extends Typenames, QP, QR, MP, MR>(actions: Actions<N, T, QP, QR, MP, MR>, queryKeys: (keyof (QP | QR))[], cacheOptions: CacheOptions) => (state: CacheState<T, QP, QR, MP, MR> | undefined, action: ReturnType<(typeof actions)[keyof typeof actions]>) => CacheState<T, QP, QR, MP, MR>;
package/dist/index.js CHANGED
@@ -27,7 +27,6 @@ Object.defineProperty(exports, "FetchPolicy", { enumerable: true, get: function
27
27
  Object.defineProperty(exports, "isEmptyObject", { enumerable: true, get: function () { return utilsAndConstants_1.isEmptyObject; } });
28
28
  // Backlog
29
29
  // ! high (1.0.0-rc.0)
30
- // reset [whole] cache to initial / to provided state
31
30
  // optimistic response
32
31
  // generate full api docs
33
32
  // ! medium
@@ -41,7 +40,6 @@ Object.defineProperty(exports, "isEmptyObject", { enumerable: true, get: functio
41
40
  // ! low
42
41
  // make error type generic
43
42
  // allow multiple mutation with same keys?
44
- // custom useStore & useSelector to support multiple stores?
45
43
  // easy access to all currently loading queries and mutations?
46
44
  // cancellation to queries
47
45
  // if mutation & query already loading - make options: last, throttle, debounce, parallel?
package/dist/mutate.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { Store } from 'redux';
2
1
  import type { Actions } from './createActions';
3
2
  import { Selectors } from './createSelectors';
4
- import type { Cache, Key, MutationResult, Typenames } from './types';
3
+ import type { Cache, Key, MutationResult, Store, Typenames } from './types';
5
4
  export declare const mutate: <N extends string, T extends Typenames, QP, QR, MP, MR, MK extends keyof (MP & MR)>(logTag: string, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>, mutationKey: MK, params: MK extends keyof (MP | MR) ? MP[MK] : never, abortControllers: WeakMap<Store, Record<Key, AbortController>>, onCompleted?: ((response: import("./types").NormalizedQueryResponse<T, MR[keyof MP & keyof MR & string]> | undefined, error: unknown | undefined, params: MP[keyof MP & keyof MR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, MR[keyof MP & keyof MR & number]> | undefined, error: unknown | undefined, params: MP[keyof MP & keyof MR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, MR[keyof MP & keyof MR & symbol]> | undefined, error: unknown | undefined, params: MP[keyof MP & keyof MR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | undefined, onSuccess?: ((response: import("./types").NormalizedQueryResponse<T, MR[keyof MP & keyof MR & string]>, params: MP[keyof MP & keyof MR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, MR[keyof MP & keyof MR & number]>, params: MP[keyof MP & keyof MR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, MR[keyof MP & keyof MR & symbol]>, params: MP[keyof MP & keyof MR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | undefined, onError?: ((error: unknown, params: MP[keyof MP & keyof MR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => boolean | void | null | undefined) | ((error: unknown, params: MP[keyof MP & keyof MR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => boolean | void | null | undefined) | ((error: unknown, params: MP[keyof MP & keyof MR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => boolean | void | null | undefined) | undefined) => Promise<MutationResult<MK extends keyof (MP | MR) ? MR[MK] : never>>;
package/dist/query.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { Store } from 'redux';
2
1
  import type { Actions } from './createActions';
3
2
  import { Selectors } from './createSelectors';
4
- import type { Cache, Key, QueryResult, Typenames } from './types';
3
+ import type { Cache, Key, QueryResult, Store, Typenames } from './types';
5
4
  export declare const query: <N extends string, T extends Typenames, QP, QR, MP, MR, QK extends keyof (QP & QR)>(logTag: string, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>, queryKey: QK, cacheKey: Key, params: QK extends keyof (QP | QR) ? QP[QK] : never, secondsToLive: number | undefined, onlyIfExpired: boolean | undefined, mergeResults?: ((oldResult: QR[keyof QP & keyof QR & string] | undefined, response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & string]>, params: QP[keyof QP & keyof QR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => QR[keyof QP & keyof QR & string]) | ((oldResult: QR[keyof QP & keyof QR & number] | undefined, response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & number]>, params: QP[keyof QP & keyof QR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => QR[keyof QP & keyof QR & number]) | ((oldResult: QR[keyof QP & keyof QR & symbol] | undefined, response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & symbol]>, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => QR[keyof QP & keyof QR & symbol]) | undefined, onCompleted?: ((response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & string]> | undefined, error: unknown | undefined, params: QP[keyof QP & keyof QR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & number]> | undefined, error: unknown | undefined, params: QP[keyof QP & keyof QR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & symbol]> | undefined, error: unknown | undefined, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | undefined, onSuccess?: ((response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & string]>, params: QP[keyof QP & keyof QR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & number]>, params: QP[keyof QP & keyof QR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | ((response: import("./types").NormalizedQueryResponse<T, QR[keyof QP & keyof QR & symbol]>, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => void) | undefined, onError?: ((error: unknown, params: QP[keyof QP & keyof QR & string] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => boolean | void | null | undefined) | ((error: unknown, params: QP[keyof QP & keyof QR & number] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => boolean | void | null | undefined) | ((error: unknown, params: QP[keyof QP & keyof QR & symbol] | undefined, store: Store, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>) => boolean | void | null | undefined) | undefined) => Promise<QueryResult<QK extends keyof (QP | QR) ? QR[QK] : never>>;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { Store } from 'redux';
2
1
  import type { Actions } from './createActions';
3
2
  import type { Selectors } from './createSelectors';
4
3
  export type Key = string | number | symbol;
@@ -6,7 +5,7 @@ export type Dict<T> = Record<Key, T>;
6
5
  export type OptionalPartial<T, K extends keyof T> = Partial<{
7
6
  [A in K]: Partial<T[A]>;
8
7
  }> & Omit<T, K>;
9
- /** Entity changes to be merged to redux state. */
8
+ /** Entity changes to be merged to the state. */
10
9
  export type EntityChanges<T extends Typenames> = {
11
10
  /** Entities that will be merged with existing. */
12
11
  merge?: PartialEntitiesMap<T>;
@@ -17,22 +16,34 @@ export type EntityChanges<T extends Typenames> = {
17
16
  /** Alias for `merge` to support normalizr. */
18
17
  entities?: EntityChanges<T>['merge'];
19
18
  };
19
+ export type Store<S = unknown> = {
20
+ dispatch: (action: ReturnType<Actions[keyof Actions]>) => unknown;
21
+ getState: () => S;
22
+ };
20
23
  /** Record of typename and its corresponding entity type */
21
24
  export type Typenames = Record<string, object>;
22
25
  export type Cache<N extends string, T extends Typenames, QP, QR, MP, MR> = {
23
- /** Used as prefix for actions and in default cacheStateSelector for selecting cache state from redux state. */
26
+ /** Used as prefix for actions and in default cacheStateSelector for selecting cache state from store root state. */
24
27
  name: N;
28
+ /** Cache options. */
29
+ options: CacheOptions;
30
+ /** Default options for queries and mutations. */
31
+ globals: Globals<N, T, QP, QR, MP, MR>;
32
+ /** Hooks to access and subscribe to the store. Imported from react-redux if not overridden. */
33
+ storeHooks: {
34
+ useStore: () => Store;
35
+ useSelector: <R>(selector: (state: unknown) => R, comparer?: (x: R, y: R) => boolean) => R;
36
+ };
37
+ /** Should return cache state from store root state. Default implementation returns `state[name]`. */
38
+ cacheStateSelector: (state: any) => CacheState<T, QP, QR, MP, MR>;
39
+ /** Queries. */
25
40
  queries: {
26
41
  [QK in keyof (QP & QR)]: QK extends keyof (QP | QR) ? QueryInfo<N, T, QP[QK], QR[QK], QP, QR, MP, MR> : never;
27
42
  };
43
+ /** Mutations. */
28
44
  mutations: {
29
45
  [MK in keyof (MP & MR)]: MK extends keyof (MP | MR) ? MutationInfo<N, T, MP[MK], MR[MK], QP, QR, MP, MR> : never;
30
46
  };
31
- /** Default options for queries and mutations. */
32
- globals: Globals<N, T, QP, QR, MP, MR>;
33
- options: CacheOptions;
34
- /** Should return cache state from redux root state. Default implementation returns `state[name]`. */
35
- cacheStateSelector: (state: any) => ReduxCacheState<T, QP, QR, MP, MR>;
36
47
  };
37
48
  export type Globals<N extends string, T extends Typenames, QP, QR, MP, MR> = {
38
49
  /** Handles errors, not handled by onError from queries and mutations. @Default undefined. */
@@ -71,7 +82,7 @@ export type EntitiesMap<T extends Typenames> = {
71
82
  export type EntityIds<T extends Typenames> = {
72
83
  [K in keyof T]?: Key[];
73
84
  };
74
- export type ReduxCacheState<T extends Typenames, QP, QR, MP, MR> = {
85
+ export type CacheState<T extends Typenames, QP, QR, MP, MR> = {
75
86
  entities: EntitiesMap<T>;
76
87
  queries: {
77
88
  [QK in keyof (QP | QR)]: Dict<QueryState<QP[QK], QR[QK]> | undefined>;
@@ -104,7 +115,7 @@ export type QueryInfo<N extends string, T extends Typenames = Typenames, P = unk
104
115
  export type Query<P = unknown, R = unknown> = (
105
116
  /** Query parameters */
106
117
  params: P,
107
- /** Redux store */
118
+ /** Store */
108
119
  store: Store) => Promise<QueryResponse<R>>;
109
120
  export type NormalizedQuery<T extends Typenames = Typenames, P = unknown, R = unknown> = (...args: Parameters<Query<P, R>>) => Promise<NormalizedQueryResponse<T, R>>;
110
121
  export type QueryState<P, R> = MutationState<P, R> & {
@@ -135,7 +146,7 @@ export type MutationInfo<N extends string, T extends Typenames = Typenames, P =
135
146
  export type Mutation<P = unknown, R = unknown> = (
136
147
  /** Mutation parameters */
137
148
  params: P,
138
- /** Redux store */
149
+ /** Store */
139
150
  store: Store,
140
151
  /** Signal is aborted for current mutation when the same mutation was called once again. */
141
152
  abortSignal: AbortSignal) => Promise<MutationResponse<R>>;
@@ -1,5 +1,4 @@
1
- import { Store } from 'redux';
2
1
  import { Actions } from './createActions';
3
2
  import { Selectors } from './createSelectors';
4
- import { Cache, Key, MutateOptions, MutationState, Typenames } from './types';
3
+ import { Cache, Key, MutateOptions, MutationState, Store, Typenames } from './types';
5
4
  export declare const useMutation: <N extends string, T extends Typenames, QP, QR, MP, MR, MK extends keyof (MP & MR)>(cache: Cache<N, T, QP, QR, MP, MR>, actions: Actions<N, T, QP, QR, MP, MR>, selectors: Selectors<N, T, QP, QR, MP, MR>, options: Omit<MutateOptions<N, T, QP, QR, 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];
@@ -11,13 +11,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.useMutation = void 0;
13
13
  const react_1 = require("react");
14
- const react_redux_1 = require("react-redux");
15
14
  const mutate_1 = require("./mutate");
16
15
  const utilsAndConstants_1 = require("./utilsAndConstants");
17
16
  const useMutation = (cache, actions, selectors, options, abortControllers) => {
18
17
  var _a;
19
18
  const { mutation: mutationKey, onCompleted, onSuccess, onError } = options;
20
- const store = (0, react_redux_1.useStore)();
19
+ const store = cache.storeHooks.useStore();
21
20
  // Using single useMemo for performance reasons
22
21
  const [mutationStateSelector, mutate, abort] = (0, react_1.useMemo)(() => {
23
22
  return [
@@ -53,7 +52,7 @@ const useMutation = (cache, actions, selectors, options, abortControllers) => {
53
52
  // eslint-disable-next-line react-hooks/exhaustive-deps
54
53
  }, [mutationKey, store]);
55
54
  // @ts-expect-error fix later
56
- const mutationState = (_a = (0, react_redux_1.useSelector)(mutationStateSelector)) !== null && _a !== void 0 ? _a : utilsAndConstants_1.EMPTY_OBJECT;
55
+ const mutationState = (_a = cache.storeHooks.useSelector(mutationStateSelector)) !== null && _a !== void 0 ? _a : utilsAndConstants_1.EMPTY_OBJECT;
57
56
  cache.options.logsEnabled &&
58
57
  (0, utilsAndConstants_1.log)('useMutation', {
59
58
  options,
package/dist/useQuery.js CHANGED
@@ -11,7 +11,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.useQuerySelectorStateComparer = exports.useQuery = void 0;
13
13
  const react_1 = require("react");
14
- const react_redux_1 = require("react-redux");
15
14
  const query_1 = require("./query");
16
15
  const utilsAndConstants_1 = require("./utilsAndConstants");
17
16
  const useQuery = (cache, actions, selectors, options) => {
@@ -20,11 +19,11 @@ const useQuery = (cache, actions, selectors, options) => {
20
19
  const logsEnabled = cache.options.logsEnabled;
21
20
  const getCacheKey = (_b = cache.queries[queryKey].getCacheKey) !== null && _b !== void 0 ? _b : (utilsAndConstants_1.defaultGetCacheKey);
22
21
  const cacheStateSelector = cache.cacheStateSelector;
23
- const store = (0, react_redux_1.useStore)();
22
+ const store = cache.storeHooks.useStore();
24
23
  // @ts-expect-error fix types later
25
24
  const cacheKey = getCacheKey(params);
26
25
  /** Fetch query with the new parameters, or refetch with the same if parameters not provided. */
27
- const fetch = (0, react_1.useCallback)((options) => __awaiter(void 0, void 0, void 0, function* () {
26
+ const performFetch = (0, react_1.useCallback)((options) => __awaiter(void 0, void 0, void 0, function* () {
28
27
  const paramsPassed = options && 'params' in options;
29
28
  return yield (0, query_1.query)('useQuery.fetch', store, cache, actions, selectors, queryKey,
30
29
  // @ts-expect-error fix later
@@ -36,7 +35,7 @@ const useQuery = (cache, actions, selectors, options) => {
36
35
  // eslint-disable-next-line react-hooks/exhaustive-deps
37
36
  [store, queryKey, cacheKey]);
38
37
  /** Query state */
39
- const queryState = (_c = (0, react_redux_1.useSelector)((state) => {
38
+ const queryState = (_c = cache.storeHooks.useSelector((state) => {
40
39
  const queryState = cacheStateSelector(state).queries[queryKey][cacheKey];
41
40
  return queryState; // TODO proper type
42
41
  }, (exports.useQuerySelectorStateComparer))) !== null && _c !== void 0 ? _c : utilsAndConstants_1.EMPTY_OBJECT;
@@ -46,8 +45,9 @@ const useQuery = (cache, actions, selectors, options) => {
46
45
  return;
47
46
  }
48
47
  const expired = queryState.expiresAt != null && queryState.expiresAt <= Date.now();
48
+ if (!fetchPolicy(expired,
49
49
  // @ts-expect-error params
50
- if (!fetchPolicy(expired, params, queryState, store, selectors)) {
50
+ params, queryState, store, selectors)) {
51
51
  logsEnabled &&
52
52
  (0, utilsAndConstants_1.log)('useQuery.useEffect skip fetch due to fetch policy', {
53
53
  queryState,
@@ -57,7 +57,7 @@ const useQuery = (cache, actions, selectors, options) => {
57
57
  });
58
58
  return;
59
59
  }
60
- fetch();
60
+ performFetch();
61
61
  // eslint-disable-next-line react-hooks/exhaustive-deps
62
62
  }, [cacheKey, skipFetch]);
63
63
  logsEnabled &&
@@ -66,7 +66,7 @@ const useQuery = (cache, actions, selectors, options) => {
66
66
  options,
67
67
  queryState,
68
68
  });
69
- return [queryState, fetch];
69
+ return [queryState, performFetch];
70
70
  };
71
71
  exports.useQuery = useQuery;
72
72
  /** Omit `expiresAt` from comparison */
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.14.0",
5
+ "version": "0.16.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",
@@ -19,12 +19,14 @@
19
19
  "peerDependencies": {
20
20
  "fast-deep-equal": "*",
21
21
  "react": "^16",
22
- "react-redux": "^4",
23
- "redux": "^4"
22
+ "react-redux": "^4"
24
23
  },
25
24
  "peerDependenciesMeta": {
26
25
  "fast-deep-equal": {
27
26
  "optional": true
27
+ },
28
+ "react-redux": {
29
+ "optional": true
28
30
  }
29
31
  },
30
32
  "devDependencies": {