react-redux-cache 0.16.1-rc.0 → 0.17.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,11 +9,13 @@
9
9
 
10
10
  # react-redux-cache (RRC)
11
11
 
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.
12
+ ### Supports both `Redux` and `Zustand` (check /example)
13
+
14
+ **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)). Covered with tests, fully typed and written on Typescript.
13
15
 
14
16
  **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
17
 
16
- Can be considered as `ApolloClient` for protocols other than `GraphQL`, but with **full control** over its storage - redux store, with ability to write custom selectors, actions and reducers to manage cached state.
18
+ Can be considered as `ApolloClient` for protocols other than `GraphQL`, but with **full control** over its storage - redux or zustand store, with ability to write custom selectors, actions and reducers to manage cached state.
17
19
 
18
20
  Examples of states, generated by cache reducer from `/example` project:
19
21
  <details>
@@ -46,7 +48,7 @@ Examples of states, generated by cache reducer from `/example` project:
46
48
  },
47
49
  getUsers: {
48
50
  // example of paginated state under custom cache key
49
- "all-pages": {
51
+ "feed": {
50
52
  result: {items: [0,1,2], page: 1},
51
53
  params: {page: 1}
52
54
  }
@@ -84,7 +86,7 @@ Examples of states, generated by cache reducer from `/example` project:
84
86
  },
85
87
  getUsers: {
86
88
  // example of paginated state under custom cache key
87
- "all-pages": {
89
+ "feed": {
88
90
  result: {
89
91
  items: [
90
92
  {id: 0, bank: {id: "0", name: "Bank 0"}, name: "User 0 *"},
@@ -130,23 +132,28 @@ Examples of states, generated by cache reducer from `/example` project:
130
132
  `react` is a peer dependency.
131
133
 
132
134
  `react-redux` and `fast-deep-equal` are optional peer dependencies:
133
- - `react-redux` required when `storeHooks` is not provided when creating cache.
135
+ - `react-redux` required when `storeHooks` is not provided when creating cache. Not needed for Zustand.
134
136
  - `fast-deep-equal` required if `deepComparisonEnabled` cache option is enabled (default is true).
135
137
 
136
138
  ```sh
137
139
  # required
138
- npm add react-redux-cache react
140
+ npm i react-redux-cache react
139
141
 
140
142
  # without react-redux
141
- npm add react-redux-cache react fast-deep-equal
143
+ npm i react-redux-cache react fast-deep-equal
142
144
 
143
145
  # all required and optional peers
144
- npm add react-redux-cache react react-redux fast-deep-equal
146
+ npm i react-redux-cache react react-redux fast-deep-equal
145
147
  ```
148
+
146
149
  ### Initialization
147
150
  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.
148
151
  All queries and mutations should be passed while initializing the cache for proper typing.
152
+
149
153
  #### cache.ts
154
+
155
+ > Zustand requires additional option - `storeHooks`.
156
+
150
157
  ```typescript
151
158
  // Mapping of all typenames to their entity types, which is needed for proper normalization typing.
152
159
  // Not needed if normalization is not used.
@@ -177,6 +184,10 @@ export const {
177
184
  updateUser: { mutation: updateUser },
178
185
  removeUser: { mutation: removeUser },
179
186
  },
187
+
188
+ // Required for Zustand. Just an empty object can be passed during initialization, and hooks can be set later (see `store.ts` section).
189
+ // Can be also used for Redux if working with multiple stores.
190
+ storeHooks: {},
180
191
  })
181
192
  ```
182
193
 
@@ -194,6 +205,8 @@ type EntityChanges<T extends Typenames> = {
194
205
  ```
195
206
 
196
207
  #### store.ts
208
+
209
+ Redux:
197
210
  ```typescript
198
211
  // Create store as usual, passing the new cache reducer under the name of the cache.
199
212
  // If some other redux structure is needed, provide custom cacheStateSelector when creating cache.
@@ -204,6 +217,24 @@ const store = configureStore({
204
217
  }
205
218
  })
206
219
  ```
220
+
221
+ Zustand:
222
+ ```typescript
223
+ type State = {[cache.name]: ReturnType<typeof reducer>}
224
+ type Actions = {dispatch: (action: Parameters<typeof reducer>[1]) => void}
225
+
226
+ const initialState = getInitialState()
227
+
228
+ export const useStore = create<State & Actions>((set, get) => ({
229
+ ...initialState,
230
+ dispatch: (action) => set({[cache.name]: reducer(get()[cache.name], action)}),
231
+ }))
232
+
233
+ const store = {dispatch: useStore.getState().dispatch, getState: useStore.getState}
234
+ cache.storeHooks.useStore = () => store
235
+ cache.storeHooks.useSelector = useStore
236
+ ```
237
+
207
238
  #### api.ts
208
239
  For normalization `normalizr` package is used in this example, but any other tool can be used if query result is of proper type.
209
240
  Perfect implementation is when the backend already returns normalized data.
@@ -239,7 +270,7 @@ export const removeUser = (async (id, _, abortSignal) => {
239
270
 
240
271
  ### Usage
241
272
 
242
- Please check `example/` folder (`npm run example` to run).
273
+ Please check `example/` folder (`npm run example` to run). There are examples for both Redux and Zustand.
243
274
 
244
275
  #### UserScreen.tsx
245
276
  ```typescript
@@ -400,7 +431,7 @@ Here is an example of `getUsers` query configuration with pagination support. Yo
400
431
  queries: {
401
432
  getUsers: {
402
433
  query: getUsers,
403
- getCacheKey: () => 'all-pages', // single cache key is used for all pages
434
+ getCacheKey: () => 'feed', // single cache key is used for all pages
404
435
  mergeResults: (oldResult, {result: newResult}) => {
405
436
  if (!oldResult || newResult.page === 1) {
406
437
  return newResult
@@ -408,7 +439,7 @@ Here is an example of `getUsers` query configuration with pagination support. Yo
408
439
  if (newResult.page === oldResult.page + 1) {
409
440
  return {
410
441
  ...newResult,
411
- items: [...oldResult.items, ...newResult.items],
442
+ items: oldResult.items.concat(newResult.items),
412
443
  }
413
444
  }
414
445
  return oldResult
@@ -175,6 +175,8 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
175
175
  useSelectEntityById: <TN extends keyof T>(id: Key | null | undefined, typename: TN) => T[TN] | undefined;
176
176
  };
177
177
  utils: {
178
+ /** Generates the initial state by calling a reducer. Not needed for redux — it already generates it the same way when creating the store. */
179
+ getInitialState: () => import("./types").CacheState<T, QP, QR, MP, MR>;
178
180
  /** Apply changes to the entities map.
179
181
  * @returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes. */
180
182
  applyEntityChanges: (entities: Parameters<typeof applyEntityChanges<T>>[0], changes: Parameters<typeof applyEntityChanges<T>>[1]) => import("./types").EntitiesMap<T> | undefined;
@@ -357,6 +359,8 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(partialCach
357
359
  useSelectEntityById: <TN extends string>(id: Key | null | undefined, typename: TN) => object | undefined;
358
360
  };
359
361
  utils: {
362
+ /** Generates the initial state by calling a reducer. Not needed for redux — it already generates it the same way when creating the store. */
363
+ getInitialState: () => import("./types").CacheState<Typenames, QP, QR, MP, MR>;
360
364
  /** Apply changes to the entities map.
361
365
  * @returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes. */
362
366
  applyEntityChanges: (entities: import("./types").EntitiesMap<Typenames>, changes: import("./types").EntityChanges<Typenames>) => import("./types").EntitiesMap<Typenames> | undefined;
@@ -58,11 +58,13 @@ const withTypenames = () => {
58
58
  // actions
59
59
  const actions = (0, createActions_1.createActions)(cache.name);
60
60
  const { updateQueryStateAndEntities, updateMutationStateAndEntities, mergeEntityChanges, invalidateQuery, clearQueryState, clearMutationState, clearCache, } = actions;
61
+ // reducer
62
+ const reducer = (0, createCacheReducer_1.createCacheReducer)(actions, Object.keys(cache.queries), cache.options);
61
63
  return {
62
64
  /** Keeps all options, passed while creating the cache. */
63
65
  cache,
64
66
  /** Reducer of the cache, should be added to redux store. */
65
- reducer: (0, createCacheReducer_1.createCacheReducer)(actions, Object.keys(cache.queries), cache.options),
67
+ reducer,
66
68
  actions: {
67
69
  /** Updates query state, and optionally merges entity changes in a single action. */
68
70
  updateQueryStateAndEntities,
@@ -147,6 +149,11 @@ const withTypenames = () => {
147
149
  },
148
150
  },
149
151
  utils: {
152
+ /** Generates the initial state by calling a reducer. Not needed for redux — it already generates it the same way when creating the store. */
153
+ getInitialState: () => {
154
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
+ return reducer(undefined, utilsAndConstants_1.EMPTY_OBJECT);
156
+ },
150
157
  /** Apply changes to the entities map.
151
158
  * @returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes. */
152
159
  applyEntityChanges: (entities, changes) => {
package/dist/types.d.ts CHANGED
@@ -119,6 +119,11 @@ params: P,
119
119
  store: Store) => Promise<QueryResponse<R>>;
120
120
  export type NormalizedQuery<T extends Typenames = Typenames, P = unknown, R = unknown> = (...args: Parameters<Query<P, R>>) => Promise<NormalizedQueryResponse<T, R>>;
121
121
  export type QueryState<P, R> = MutationState<P, R> & {
122
+ /**
123
+ * Timestamp in milliseconds, after which state is considered expired.
124
+ * Hooks may refetch the query again when component mounts, cache key or skip option change, depending on the fetch policy.
125
+ * Client query calls also start making fetch if onlyIfExpired argument is truthy.
126
+ * */
122
127
  expiresAt?: number;
123
128
  };
124
129
  export type UseQueryOptions<N extends string, T extends Typenames, QK extends keyof (QP & QR), QP, QR, MP, MR> = {
package/package.json CHANGED
@@ -2,14 +2,15 @@
2
2
  "name": "react-redux-cache",
3
3
  "author": "Alexander Danilov",
4
4
  "license": "MIT",
5
- "version": "0.16.1-rc.0",
6
- "description": "Powerful data fetching and caching library that supports normalization, built on top of redux",
5
+ "version": "0.17.0",
6
+ "description": "Powerful data fetching and caching library for Redux and Zustand that supports normalization.",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
9
9
  "scripts": {
10
- "example": "(cd example && yarn && yarn start)",
10
+ "example": "(cd example && yarn && yarn dev)",
11
11
  "clean": "rm -rf dist",
12
12
  "lint": "yarn eslint src",
13
+ "lint-fix": "yarn eslint --fix src",
13
14
  "build": "yarn clean && yarn lint && tsc && rm -rf dist/testing && rm -rf dist/__tests__",
14
15
  "test": "node node_modules/jest/bin/jest.js",
15
16
  "preminor-rc": "yarn build && npm version preminor --preid rc",
@@ -65,6 +66,7 @@
65
66
  "keywords": [
66
67
  "react",
67
68
  "redux",
69
+ "zustand",
68
70
  "cache",
69
71
  "query",
70
72
  "normalization"