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 +42 -11
- package/dist/createCache.d.ts +4 -0
- package/dist/createCache.js +8 -1
- package/dist/types.d.ts +5 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
# react-redux-cache (RRC)
|
|
11
11
|
|
|
12
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
140
|
+
npm i react-redux-cache react
|
|
139
141
|
|
|
140
142
|
# without react-redux
|
|
141
|
-
npm
|
|
143
|
+
npm i react-redux-cache react fast-deep-equal
|
|
142
144
|
|
|
143
145
|
# all required and optional peers
|
|
144
|
-
npm
|
|
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: () => '
|
|
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:
|
|
442
|
+
items: oldResult.items.concat(newResult.items),
|
|
412
443
|
}
|
|
413
444
|
}
|
|
414
445
|
return oldResult
|
package/dist/createCache.d.ts
CHANGED
|
@@ -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;
|
package/dist/createCache.js
CHANGED
|
@@ -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
|
|
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.
|
|
6
|
-
"description": "Powerful data fetching and caching library
|
|
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
|
|
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"
|