react-redux-cache 0.2.0 → 0.4.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
@@ -1,18 +1,88 @@
1
- ### Donations 🙌
2
-
3
- **BTC:** bc1qs0sq7agz5j30qnqz9m60xj4tt8th6aazgw7kxr \
4
- **ETH:** 0x1D834755b5e889703930AC9b784CB625B3cd833E \
5
- **USDT(Tron):** TPrCq8LxGykQ4as3o1oB8V7x1w2YPU2o5n \
6
- **TON:** EQAtBuFWI3H_LpHfEToil4iYemtfmyzlaJpahM3tFSoxojvV \
7
- **DOGE:** D7GMQdKhKC9ymbT9PtcetSFTQjyPRRfkwT
1
+ <details>
2
+ <summary>Donations 🙌</summary>
3
+ <b>BTC:</b> bc1qs0sq7agz5j30qnqz9m60xj4tt8th6aazgw7kxr <br>
4
+ <b>ETH:</b> 0x1D834755b5e889703930AC9b784CB625B3cd833E <br>
5
+ <b>USDT(Tron):</b> TPrCq8LxGykQ4as3o1oB8V7x1w2YPU2o5n <br>
6
+ <b>TON:</b> EQAtBuFWI3H_LpHfEToil4iYemtfmyzlaJpahM3tFSoxojvV <br>
7
+ <b>DOGE:</b> D7GMQdKhKC9ymbT9PtcetSFTQjyPRRfkwT <br>
8
+ </details>
8
9
 
9
10
  # react-redux-cache
10
11
 
11
- **Powerful** yet **lightweight** data fetching and caching library that supports **normalization** unlike `react-query` and `rtk-query`, while having similar but very simple interface. Built on top of `redux`, fully typed and written on Typescript. Can be considered as `ApolloClient` for protocols other than `GraphQL`.
12
+ **Powerful** yet **lightweight** data fetching and caching library that supports **normalization** unlike `react-query` and `rtk-query`, while having similar but very simple interface. Built on top of `redux`, covered with tests, fully typed and written on Typescript.
12
13
 
13
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**.
14
15
 
15
- Remains **full control** of redux state with ability to write custom selectors, actions and reducers to manage cached state.
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.
17
+
18
+ Example of **normalized** redux state, generated by cache reducer from `/example` project:
19
+ ```js
20
+ {
21
+ entities: {
22
+ // each typename has its own map of entities, stored by id
23
+ users: {
24
+ "0": {id: 0, bankId: "0", name: "User 0 *"},
25
+ "1": {id: 1, bankId: "1", name: "User 1 *"},
26
+ "2": {id: 2, bankId: "2", name: "User 2"},
27
+ "3": {id: 3, bankId: "3", name: "User 3"}
28
+ },
29
+ banks: {
30
+ "0": {id: "0", name: "Bank 0"},
31
+ "1": {id: "1", name: "Bank 1"},
32
+ "2": {id: "2", name: "Bank 2"},
33
+ "3": {id: "3", name: "Bank 3"}
34
+ }
35
+ },
36
+ queries: {
37
+ // each query has its own map of query states, stored by cache key, which is generated from query params
38
+ getUser: {
39
+ "2": {loading: false, error: undefined, result: 2},
40
+ "3": {loading: true}
41
+ },
42
+ getUsers: {
43
+ // example of paginated state under custom cache key
44
+ "all-pages": {loading: false, result: {items: [0,1,2], page: 1}}
45
+ }
46
+ },
47
+ mutations: {
48
+ // each mutation has its own state as well
49
+ updateUser: {loading: false, result: 1}
50
+ }
51
+ }
52
+ ```
53
+
54
+ Example of **not normalized** redux state, generated by cache reducer from `/example` project:
55
+ ```js
56
+ {
57
+ // entities map is used for normalization and is empty here
58
+ entities: {},
59
+ queries: {
60
+ // each query has its own map of query states, stored by cache key, which is generated from query params
61
+ getUser: {
62
+ "2": {loading: false, error: undefined, result: {id: 2, bank: {id: "2", name: "Bank 2"}, name: "User 2"}},
63
+ "3": {loading: true}
64
+ },
65
+ getUsers: {
66
+ // example of paginated state under custom cache key
67
+ "all-pages": {
68
+ loading: false,
69
+ result: {
70
+ items: [
71
+ {id: 0, bank: {id: "0", name: "Bank 0"}, name: "User 0 *"},
72
+ {id: 1, bank: {id: "1", name: "Bank 1"}, name: "User 1 *"},
73
+ {id: 2, bank: {id: "2", name: "Bank 2"}, name: "User 2"}
74
+ ],
75
+ page: 1
76
+ }
77
+ }
78
+ }
79
+ },
80
+ mutations: {
81
+ // each mutation has its own state as well
82
+ updateUser: {loading: false, result: {id: 1, bank: {id: "1", name: "Bank 1"}, name: "User 1 *"}}
83
+ }
84
+ }
85
+ ```
16
86
 
17
87
  ### Table of contents
18
88
 
@@ -43,17 +113,7 @@ All typenames, queries and mutations should be passed while initializing the cac
43
113
  export const {
44
114
  cache,
45
115
  reducer,
46
- hooks: {useClient, useMutation, useQuery, useSelectEntityById},
47
- // Actions, selectors and utils may be not used at all
48
- selectors: {entitiesSelector, entitiesByTypenameSelector},
49
- actions: {
50
- updateQueryStateAndEntities,
51
- updateMutationStateAndEntities,
52
- mergeEntityChanges,
53
- clearQueryState,
54
- clearMutationState,
55
- },
56
- utils: {applyEntityChanges},
116
+ hooks: {useClient, useMutation, useQuery},
57
117
  } = createCache({
58
118
  // Used as prefix for actions and in default cacheStateSelector for selecting cache state from redux state.
59
119
  name: 'cache',
@@ -1,6 +1,7 @@
1
1
  import type { EntityChanges, Key, QueryMutationState, Typenames } from './types';
2
2
  export type ActionMap<N extends string, T extends Typenames, QR, MR> = ReturnType<typeof createActions<N, T, QR, MR>>;
3
3
  export declare const createActions: <N extends string, T extends Typenames, QR, MR>(name: N) => {
4
+ /** Updates query state, and optionally merges entity changes in a single action. */
4
5
  updateQueryStateAndEntities: {
5
6
  <K extends keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<QueryMutationState<QR[K]>> | undefined, entityChagnes?: EntityChanges<T> | undefined): {
6
7
  type: `@rrc/${N}/updateQueryStateAndEntities`;
@@ -11,6 +12,7 @@ export declare const createActions: <N extends string, T extends Typenames, QR,
11
12
  };
12
13
  type: `@rrc/${N}/updateQueryStateAndEntities`;
13
14
  };
15
+ /** Updates mutation state, and optionally merges entity changes in a single action. */
14
16
  updateMutationStateAndEntities: {
15
17
  <K_1 extends keyof MR>(mutationKey: K_1, state?: Partial<QueryMutationState<MR[K_1]>> | undefined, entityChagnes?: EntityChanges<T> | undefined): {
16
18
  type: `@rrc/${N}/updateMutationStateAndEntities`;
@@ -20,6 +22,7 @@ export declare const createActions: <N extends string, T extends Typenames, QR,
20
22
  };
21
23
  type: `@rrc/${N}/updateMutationStateAndEntities`;
22
24
  };
25
+ /** Merge EntityChanges to the state. */
23
26
  mergeEntityChanges: {
24
27
  (changes: EntityChanges<T>): {
25
28
  type: `@rrc/${N}/mergeEntityChanges`;
@@ -27,6 +30,8 @@ export declare const createActions: <N extends string, T extends Typenames, QR,
27
30
  };
28
31
  type: `@rrc/${N}/mergeEntityChanges`;
29
32
  };
33
+ /** Clear states for provided query keys and cache keys.
34
+ * If cache key for query key is not provided, the whole state for query key is cleared. */
30
35
  clearQueryState: {
31
36
  <K_2 extends keyof QR>(queryKeys: {
32
37
  key: K_2;
@@ -40,6 +45,7 @@ export declare const createActions: <N extends string, T extends Typenames, QR,
40
45
  };
41
46
  type: `@rrc/${N}/clearQueryState`;
42
47
  };
48
+ /** Clear states for provided mutation keys. */
43
49
  clearMutationState: {
44
50
  <K_3 extends keyof MR>(mutationKeys: K_3[]): {
45
51
  type: `@rrc/${N}/clearMutationState`;
@@ -5,7 +5,6 @@ const utilsAndConstants_1 = require("./utilsAndConstants");
5
5
  const createActions = (name) => {
6
6
  const actionPrefix = `@${utilsAndConstants_1.PACKAGE_SHORT_NAME}/${name}/`;
7
7
  const updateQueryStateAndEntitiesType = `${actionPrefix}updateQueryStateAndEntities`;
8
- /** Updates query state, and optionally merges entity changes in a single action. */
9
8
  const updateQueryStateAndEntities = (queryKey, queryCacheKey, state, entityChagnes) => ({
10
9
  type: updateQueryStateAndEntitiesType,
11
10
  queryKey,
@@ -15,7 +14,6 @@ const createActions = (name) => {
15
14
  });
16
15
  updateQueryStateAndEntities.type = updateQueryStateAndEntitiesType;
17
16
  const updateMutationStateAndEntitiesType = `${actionPrefix}updateMutationStateAndEntities`;
18
- /** Updates mutation state, and optionally merges entity changes in a single action. */
19
17
  const updateMutationStateAndEntities = (mutationKey, state, entityChagnes) => ({
20
18
  type: updateMutationStateAndEntitiesType,
21
19
  mutationKey,
@@ -24,32 +22,34 @@ const createActions = (name) => {
24
22
  });
25
23
  updateMutationStateAndEntities.type = updateMutationStateAndEntitiesType;
26
24
  const mergeEntityChangesType = `${actionPrefix}mergeEntityChanges`;
27
- /** Merge EntityChanges to the state. */
28
25
  const mergeEntityChanges = (changes) => ({
29
26
  type: mergeEntityChangesType,
30
27
  changes,
31
28
  });
32
29
  mergeEntityChanges.type = mergeEntityChangesType;
33
30
  const clearQueryStateType = `${actionPrefix}clearQueryState`;
34
- /** Clear states for provided query keys and cache keys.
35
- * If cache key for query key is not provided, the whole state for query key is cleared. */
36
31
  const clearQueryState = (queryKeys) => ({
37
32
  type: clearQueryStateType,
38
33
  queryKeys,
39
34
  });
40
35
  clearQueryState.type = clearQueryStateType;
41
36
  const clearMutationStateType = `${actionPrefix}clearMutationState`;
42
- /** Clear states for provided mutation keys. */
43
37
  const clearMutationState = (mutationKeys) => ({
44
38
  type: clearMutationStateType,
45
39
  mutationKeys,
46
40
  });
47
41
  clearMutationState.type = clearMutationStateType;
48
42
  return {
43
+ /** Updates query state, and optionally merges entity changes in a single action. */
49
44
  updateQueryStateAndEntities,
45
+ /** Updates mutation state, and optionally merges entity changes in a single action. */
50
46
  updateMutationStateAndEntities,
47
+ /** Merge EntityChanges to the state. */
51
48
  mergeEntityChanges,
49
+ /** Clear states for provided query keys and cache keys.
50
+ * If cache key for query key is not provided, the whole state for query key is cleared. */
52
51
  clearQueryState,
52
+ /** Clear states for provided mutation keys. */
53
53
  clearMutationState,
54
54
  };
55
55
  };
@@ -1,4 +1,4 @@
1
- import type { Cache, EntitiesMap, Key, MutationResult, OptionalPartial, QueryOptions, QueryResult, Typenames } from './types';
1
+ import type { Cache, Key, MutationResult, OptionalPartial, QueryMutationState, QueryOptions, QueryResult, Typenames } from './types';
2
2
  import { useMutation } from './useMutation';
3
3
  import { useQuery } from './useQuery';
4
4
  import { applyEntityChanges } from './utilsAndConstants';
@@ -6,22 +6,23 @@ import { applyEntityChanges } from './utilsAndConstants';
6
6
  * Creates reducer, actions and hooks for managing queries and mutations through redux cache.
7
7
  */
8
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">) => {
9
+ /** Keeps all options, passed while creating the cache. */
9
10
  cache: Cache<N, T, QP, QR, MP, MR>;
10
11
  /** Reducer of the cache, should be added to redux store. */
11
12
  reducer: (state: {
12
- entities: EntitiesMap<T>;
13
- queries: { [QK in keyof QR]: import("./types").Dict<import("./types").QueryMutationState<QR[QK]>>; };
14
- mutations: { [MK in keyof MR]: import("./types").QueryMutationState<MR[MK]>; };
13
+ entities: import("./types").EntitiesMap<T>;
14
+ queries: { [QK in keyof QR]: import("./types").Dict<QueryMutationState<QR[QK]>>; };
15
+ mutations: { [MK in keyof MR]: QueryMutationState<MR[MK]>; };
15
16
  } | undefined, action: {
16
17
  type: `@rrc/${N}/updateQueryStateAndEntities`;
17
18
  queryKey: keyof QR;
18
19
  queryCacheKey: Key;
19
- state: Partial<import("./types").QueryMutationState<QR[keyof QR]>> | undefined;
20
+ state: Partial<QueryMutationState<QR[keyof QR]>> | undefined;
20
21
  entityChagnes: import("./types").EntityChanges<T> | undefined;
21
22
  } | {
22
23
  type: `@rrc/${N}/updateMutationStateAndEntities`;
23
24
  mutationKey: keyof MR;
24
- state: Partial<import("./types").QueryMutationState<MR[keyof MR]>> | undefined;
25
+ state: Partial<QueryMutationState<MR[keyof MR]>> | undefined;
25
26
  entityChagnes: import("./types").EntityChanges<T> | undefined;
26
27
  } | {
27
28
  type: `@rrc/${N}/mergeEntityChanges`;
@@ -34,28 +35,28 @@ export declare const createCache: <N extends string, T extends Typenames, QP, QR
34
35
  }[];
35
36
  } | {
36
37
  type: `@rrc/${N}/clearMutationState`;
37
- mutationKeys: (keyof MR)[]; /** Select all entities of provided typename. */
38
+ mutationKeys: (keyof MR)[];
38
39
  }) => {
39
- entities: EntitiesMap<T>;
40
- queries: { [QK in keyof QR]: import("./types").Dict<import("./types").QueryMutationState<QR[QK]>>; };
41
- mutations: { [MK in keyof MR]: import("./types").QueryMutationState<MR[MK]>; };
40
+ entities: import("./types").EntitiesMap<T>;
41
+ queries: { [QK in keyof QR]: import("./types").Dict<QueryMutationState<QR[QK]>>; };
42
+ mutations: { [MK in keyof MR]: QueryMutationState<MR[MK]>; };
42
43
  };
43
44
  actions: {
44
45
  updateQueryStateAndEntities: {
45
- <K extends keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<import("./types").QueryMutationState<QR[K]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined): {
46
+ <K extends keyof QR>(queryKey: K, queryCacheKey: Key, state?: Partial<QueryMutationState<QR[K]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined): {
46
47
  type: `@rrc/${N}/updateQueryStateAndEntities`;
47
48
  queryKey: K;
48
49
  queryCacheKey: Key;
49
- state: Partial<import("./types").QueryMutationState<QR[K]>> | undefined;
50
+ state: Partial<QueryMutationState<QR[K]>> | undefined;
50
51
  entityChagnes: import("./types").EntityChanges<T> | undefined;
51
52
  };
52
53
  type: `@rrc/${N}/updateQueryStateAndEntities`;
53
54
  };
54
55
  updateMutationStateAndEntities: {
55
- <K_1 extends keyof MR>(mutationKey: K_1, state?: Partial<import("./types").QueryMutationState<MR[K_1]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined): {
56
+ <K_1 extends keyof MR>(mutationKey: K_1, state?: Partial<QueryMutationState<MR[K_1]>> | undefined, entityChagnes?: import("./types").EntityChanges<T> | undefined): {
56
57
  type: `@rrc/${N}/updateMutationStateAndEntities`;
57
58
  mutationKey: K_1;
58
- state: Partial<import("./types").QueryMutationState<MR[K_1]>> | undefined;
59
+ state: Partial<QueryMutationState<MR[K_1]>> | undefined;
59
60
  entityChagnes: import("./types").EntityChanges<T> | undefined;
60
61
  };
61
62
  type: `@rrc/${N}/updateMutationStateAndEntities`;
@@ -83,36 +84,56 @@ export declare const createCache: <N extends string, T extends Typenames, QP, QR
83
84
  clearMutationState: {
84
85
  <K_3 extends keyof MR>(mutationKeys: K_3[]): {
85
86
  type: `@rrc/${N}/clearMutationState`;
86
- mutationKeys: K_3[]; /** Select all entities of provided typename. */
87
+ mutationKeys: K_3[];
87
88
  };
88
89
  type: `@rrc/${N}/clearMutationState`;
89
- };
90
+ }; /** Selects query latest result. */
90
91
  };
91
92
  selectors: {
92
- /** Select all entities from the state. */
93
- entitiesSelector: (state: unknown) => EntitiesMap<T>;
94
- /** Select all entities of provided typename. */
95
- entitiesByTypenameSelector: <TN extends keyof T>(typename: TN) => { [K_4 in keyof T]: (state: unknown) => EntitiesMap<T>[K_4]; }[TN];
93
+ /** Selects query state. */
94
+ selectQueryState: <QK_1 extends keyof QR | keyof QP>(state: unknown, query: QK_1, cacheKey: Key) => QueryMutationState<QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never> | undefined;
95
+ /** Selects query latest result. */
96
+ selectQueryResult: <QK_2 extends keyof QR | keyof QP>(state: unknown, query: QK_2, cacheKey: Key) => (QK_2 extends keyof QP & keyof QR ? QR[QK_2] : never) | undefined;
97
+ /** Selects query loading state. */
98
+ selectQueryLoading: <QK_3 extends keyof QR | keyof QP>(state: unknown, query: QK_3, cacheKey: Key) => boolean | undefined;
99
+ /** Selects query latest error. */
100
+ selectQueryError: <QK_4 extends keyof QR | keyof QP>(state: unknown, query: QK_4, cacheKey: Key) => Error | undefined;
101
+ /** Selects mutation state. */
102
+ selectMutationState: <MK_1 extends keyof MP | keyof MR>(state: unknown, mutation: MK_1) => QueryMutationState<MK_1 extends keyof MP & keyof MR ? MR[MK_1] : never>;
103
+ /** Selects mutation latest result. */
104
+ 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;
105
+ /** Selects mutation loading state. */
106
+ selectMutationLoading: <MK_3 extends keyof MP | keyof MR>(state: unknown, mutation: MK_3) => boolean;
107
+ /** Selects mutation latest error. */
108
+ selectMutationError: <MK_4 extends keyof MP | keyof MR>(state: unknown, mutation: MK_4) => Error | undefined;
109
+ /** Selects entity by id and typename. */
110
+ selectEntityById: <TN extends keyof T>(state: unknown, id: Key | null | undefined, typename: TN) => void;
111
+ /** Selects all entities. */
112
+ selectEntities: (state: unknown) => import("./types").EntitiesMap<T>;
113
+ /** Selects all entities of provided typename. */
114
+ selectEntitiesByTypename: <TN_1 extends keyof T>(state: unknown, typename: TN_1) => import("./types").EntitiesMap<T>[TN_1];
96
115
  };
97
116
  hooks: {
98
- /** Returns client object with query function */
117
+ /** Returns client object with query and mutate functions. */
99
118
  useClient: () => {
100
- query: <QK_1 extends keyof QR | keyof QP>(options: QueryOptions<T, QP, QR, MR, QK_1>) => Promise<QueryResult<QK_1 extends keyof QP & keyof QR ? QR[QK_1] : never>>;
101
- mutate: <MK_1 extends keyof MP | keyof MR>(options: {
102
- mutation: MK_1;
103
- params: MK_1 extends keyof MP & keyof MR ? MP[MK_1] : never;
104
- }) => Promise<MutationResult<MK_1 extends keyof MP & keyof MR ? MR[MK_1] : never>>;
119
+ query: <QK_5 extends keyof QR | keyof QP>(options: Omit<QueryOptions<T, QP, QR, MR, QK_5>, "cachePolicy">) => Promise<QueryResult<QK_5 extends keyof QP & keyof QR ? QR[QK_5] : never>>;
120
+ mutate: <MK_5 extends keyof MP | keyof MR>(options: {
121
+ mutation: MK_5;
122
+ params: MK_5 extends keyof MP & keyof MR ? MP[MK_5] : never;
123
+ }) => Promise<MutationResult<MK_5 extends keyof MP & keyof MR ? MR[MK_5] : never>>;
105
124
  };
106
125
  /** Fetches query when params change and subscribes to query state. */
107
- useQuery: <QK_2 extends keyof QR | keyof QP>(options: import("./types").UseQueryOptions<T, QP, QR, MR, QK_2>) => readonly [import("./types").QueryMutationState<QK_2 extends keyof QP & keyof QR ? QR[QK_2] : never>, () => Promise<void>];
126
+ useQuery: <QK_6 extends keyof QR | keyof QP>(options: import("./types").UseQueryOptions<T, QP, QR, MR, QK_6>) => readonly [QueryMutationState<QK_6 extends keyof QP & keyof QR ? QR[QK_6] : never>, () => Promise<QueryResult<QK_6 extends infer T_1 ? T_1 extends QK_6 ? T_1 extends keyof QP & keyof QR ? QR[T_1] : never : never : never>>];
108
127
  /** Subscribes to provided mutation state and provides mutate function. */
109
- useMutation: <MK_2 extends keyof MP | keyof MR>(options: {
110
- mutation: MK_2;
111
- }) => readonly [(params: MK_2 extends keyof MP & keyof MR ? MP[MK_2] : never) => Promise<void>, import("./types").QueryMutationState<MK_2 extends keyof MP & keyof MR ? MP[MK_2] : never>, () => boolean];
112
- /** Selects entity by id and subscribes to the changes. */
113
- useSelectEntityById: <TN_1 extends keyof T>(id: Key | null | undefined, typename: TN_1) => T[TN_1] | undefined;
128
+ useMutation: <MK_6 extends keyof MP | keyof MR>(options: {
129
+ mutation: MK_6;
130
+ }) => readonly [(params: MK_6 extends keyof MP & keyof MR ? MP[MK_6] : never) => Promise<MutationResult<MK_6 extends infer T_2 ? T_2 extends MK_6 ? T_2 extends keyof MP & keyof MR ? MR[T_2] : never : never : never>>, QueryMutationState<MK_6 extends keyof MP & keyof MR ? MP[MK_6] : never>, () => boolean];
114
131
  };
115
132
  utils: {
116
- applyEntityChanges: (entities: EntitiesMap<T>, changes: import("./types").EntityChanges<T>) => EntitiesMap<T> | undefined;
133
+ /**
134
+ * Apply changes to the entities map.
135
+ * @return `undefined` if nothing to change, otherwise new entities map with applied changes.
136
+ */
137
+ applyEntityChanges: (entities: import("./types").EntitiesMap<T>, changes: import("./types").EntityChanges<T>) => import("./types").EntitiesMap<T> | undefined;
117
138
  };
118
139
  };
@@ -29,29 +29,68 @@ const createCache = (partialCache) => {
29
29
  partialCache.abortControllers = abortControllers;
30
30
  const cache = partialCache;
31
31
  // make selectors
32
- const entitiesSelector = (state) => {
33
- return cache.cacheStateSelector(state).entities;
32
+ const selectQueryState = (state, query, cacheKey) => {
33
+ // @ts-expect-error fix later
34
+ return cache.cacheStateSelector(state).queries[query][cacheKey];
35
+ };
36
+ const selectMutationState = (state, mutation) => {
37
+ // @ts-expect-error fix later
38
+ return cache.cacheStateSelector(state).mutations[mutation];
34
39
  };
35
- const enitityMapSelectorByTypename = Object.keys(partialCache.typenames).reduce((result, x) => {
36
- result[x] = (state) => cache.cacheStateSelector(state).entities[x];
37
- return result;
38
- }, {});
39
40
  const actions = (0, createActions_1.createActions)(cache.name);
40
41
  return {
42
+ /** Keeps all options, passed while creating the cache. */
41
43
  cache,
42
44
  /** Reducer of the cache, should be added to redux store. */
43
45
  reducer: (0, reducer_1.createCacheReducer)(actions, cache.typenames, Object.keys(cache.queries), cache.options),
44
46
  actions,
45
47
  selectors: {
46
- /** Select all entities from the state. */
47
- entitiesSelector,
48
- /** Select all entities of provided typename. */
49
- entitiesByTypenameSelector: (typename) => {
50
- return enitityMapSelectorByTypename[typename];
48
+ /** Selects query state. */
49
+ selectQueryState,
50
+ /** Selects query latest result. */
51
+ selectQueryResult: (state, query, cacheKey) => {
52
+ var _a;
53
+ return (_a = selectQueryState(state, query, cacheKey)) === null || _a === void 0 ? void 0 : _a.result;
54
+ },
55
+ /** Selects query loading state. */
56
+ selectQueryLoading: (state, query, cacheKey) => {
57
+ var _a;
58
+ return (_a = selectQueryState(state, query, cacheKey)) === null || _a === void 0 ? void 0 : _a.loading;
59
+ },
60
+ /** Selects query latest error. */
61
+ selectQueryError: (state, query, cacheKey) => {
62
+ var _a;
63
+ return (_a = selectQueryState(state, query, cacheKey)) === null || _a === void 0 ? void 0 : _a.error;
64
+ },
65
+ /** Selects mutation state. */
66
+ selectMutationState,
67
+ /** Selects mutation latest result. */
68
+ selectMutationResult: (state, mutation) => {
69
+ return selectMutationState(state, mutation).result;
70
+ },
71
+ /** Selects mutation loading state. */
72
+ selectMutationLoading: (state, mutation) => {
73
+ return selectMutationState(state, mutation).loading;
74
+ },
75
+ /** Selects mutation latest error. */
76
+ selectMutationError: (state, mutation) => {
77
+ return selectMutationState(state, mutation).error;
78
+ },
79
+ /** Selects entity by id and typename. */
80
+ selectEntityById: (state, id, typename) => {
81
+ id == null ? undefined : cache.cacheStateSelector(state).entities[typename][id];
82
+ },
83
+ /** Selects all entities. */
84
+ selectEntities: (state) => {
85
+ return cache.cacheStateSelector(state).entities;
86
+ },
87
+ /** Selects all entities of provided typename. */
88
+ selectEntitiesByTypename: (state, typename) => {
89
+ return cache.cacheStateSelector(state).entities[typename];
51
90
  },
52
91
  },
53
92
  hooks: {
54
- /** Returns client object with query function */
93
+ /** Returns client object with query and mutate functions. */
55
94
  useClient: () => {
56
95
  const store = (0, react_redux_1.useStore)();
57
96
  return (0, react_1.useMemo)(() => {
@@ -62,10 +101,10 @@ const createCache = (partialCache) => {
62
101
  const getCacheKey = (_a = cache.queries[queryKey].getCacheKey) !== null && _a !== void 0 ? _a : (utilsAndConstants_1.defaultGetCacheKey);
63
102
  // @ts-expect-error fix later
64
103
  const cacheKey = getCacheKey(params);
65
- return (0, query_1.query)('query', true, store, cache, actions, queryKey, cacheKey, params);
104
+ return (0, query_1.query)('query', store, cache, actions, queryKey, cacheKey, params);
66
105
  },
67
106
  mutate: (options) => {
68
- return (0, mutate_1.mutate)('mutate', true, store, cache, actions, options.mutation, options.params, abortControllers);
107
+ return (0, mutate_1.mutate)('mutate', store, cache, actions, options.mutation, options.params, abortControllers);
69
108
  },
70
109
  };
71
110
  return client;
@@ -75,14 +114,12 @@ const createCache = (partialCache) => {
75
114
  useQuery: (options) => (0, useQuery_1.useQuery)(cache, actions, options),
76
115
  /** Subscribes to provided mutation state and provides mutate function. */
77
116
  useMutation: (options) => (0, useMutation_1.useMutation)(cache, actions, options, abortControllers),
78
- /** Selects entity by id and subscribes to the changes. */
79
- useSelectEntityById: (id, typename) => {
80
- return (0, react_redux_1.useSelector)((state) =>
81
- // TODO move to selectors?
82
- id == null ? undefined : cache.cacheStateSelector(state).entities[typename][id]);
83
- },
84
117
  },
85
118
  utils: {
119
+ /**
120
+ * Apply changes to the entities map.
121
+ * @return `undefined` if nothing to change, otherwise new entities map with applied changes.
122
+ */
86
123
  applyEntityChanges: (entities, changes) => {
87
124
  return (0, utilsAndConstants_1.applyEntityChanges)(entities, changes, cache.options);
88
125
  },
package/dist/index.js CHANGED
@@ -23,15 +23,11 @@ Object.defineProperty(exports, "defaultGetCacheKey", { enumerable: true, get: fu
23
23
  Object.defineProperty(exports, "defaultQueryMutationState", { enumerable: true, get: function () { return utilsAndConstants_1.DEFAULT_QUERY_MUTATION_STATE; } });
24
24
  // Backlog
25
25
  // ! high
26
- // screenshot of redux state to README
27
- // optimistic response
28
- // cover with tests
26
+ // add not optimized not normalized example, and not normalized state example to readme
29
27
  // try use skip for refreshing strategy?
30
- // add example without normalization
31
- // make query key / cache key difference more clear in the docs
32
- // support multiple caches = reducers
28
+ // optimistic response
29
+ // make query key / cache key difference more clear in the docs, and/or rename queryKey -> query
33
30
  // ! medium
34
- // make named caches to produce named hooks, actions etc (same as slices in RTK)?
35
31
  // allow multiple mutation with same keys?
36
32
  // type extractors from cache
37
33
  // custom useStore
@@ -45,6 +41,7 @@ Object.defineProperty(exports, "defaultQueryMutationState", { enumerable: true,
45
41
  // make error type generic
46
42
  // don't cache result if resultSelector set? throw error if mergeResult set with resultSelector?
47
43
  // ! low
44
+ // access to currently loading queries and mutations?
48
45
  // add params to the state?
49
46
  // cancellation to queries
50
47
  // if mutation & query alrady loading - make options: last, throttle, debounce, parallel?
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, returnResult: boolean, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, { updateMutationStateAndEntities }: Pick<ActionMap<N, T, QR, MR>, "updateMutationStateAndEntities">, mutationKey: MK, params: MK extends keyof MP & keyof MR ? MP[MK] : never, abortControllers: WeakMap<Store, Record<Key, AbortController>>) => Promise<void | 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, QR, 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>>;
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, returnResult, store, cache, { updateMutationStateAndEntities }, mutationKey, params, abortControllers) => __awaiter(void 0, void 0, void 0, function* () {
14
+ const mutate = (logTag, store, cache, { updateMutationStateAndEntities }, mutationKey, params, abortControllers) => __awaiter(void 0, void 0, void 0, function* () {
15
15
  let abortControllersOfStore = abortControllers.get(store);
16
16
  if (abortControllersOfStore === undefined) {
17
17
  abortControllersOfStore = {};
@@ -55,7 +55,7 @@ const mutate = (logTag, returnResult, store, cache, { updateMutationStateAndEnti
55
55
  aborted: abortController.signal.aborted,
56
56
  });
57
57
  if (abortController.signal.aborted) {
58
- return returnResult ? { aborted: true } : undefined;
58
+ return ABORTED_RESULT;
59
59
  }
60
60
  delete abortControllersOfStore[mutationKey];
61
61
  if (error) {
@@ -73,8 +73,9 @@ const mutate = (logTag, returnResult, store, cache, { updateMutationStateAndEnti
73
73
  result: response.result,
74
74
  }, response));
75
75
  // @ts-expect-error fix later
76
- return returnResult ? { result: response.result } : undefined;
76
+ return { result: response.result };
77
77
  }
78
78
  throw new Error(`${logTag}: both error and response are not defined`);
79
79
  });
80
80
  exports.mutate = mutate;
81
+ const ABORTED_RESULT = Object.freeze({ aborted: true });
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, returnResult: boolean, store: Store, cache: Cache<N, T, QP, QR, MP, MR>, { updateQueryStateAndEntities }: Pick<ActionMap<N, T, QR, MR>, "updateQueryStateAndEntities">, queryKey: QK, cacheKey: Key, params: QK extends keyof QP & keyof QR ? QP[QK] : never) => Promise<void | 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, QR, MR>, "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>>;
package/dist/query.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.query = void 0;
13
13
  const utilsAndConstants_1 = require("./utilsAndConstants");
14
- const query = (logTag, returnResult, store, cache, { updateQueryStateAndEntities }, queryKey, cacheKey, params) => __awaiter(void 0, void 0, void 0, function* () {
14
+ const query = (logTag, store, cache, { updateQueryStateAndEntities }, queryKey, cacheKey, params) => __awaiter(void 0, void 0, void 0, function* () {
15
15
  var _a;
16
16
  const logsEnabled = cache.options.logsEnabled;
17
17
  const cacheStateSelector = cache.cacheStateSelector;
@@ -25,7 +25,7 @@ const query = (logTag, returnResult, store, cache, { updateQueryStateAndEntities
25
25
  params,
26
26
  cacheKey,
27
27
  });
28
- return returnResult ? { cancelled: true } : undefined;
28
+ return CANCELLED_RESULT;
29
29
  }
30
30
  store.dispatch(updateQueryStateAndEntities(queryKey, cacheKey, {
31
31
  loading: true,
@@ -43,7 +43,7 @@ const query = (logTag, returnResult, store, cache, { updateQueryStateAndEntities
43
43
  error: error,
44
44
  loading: false,
45
45
  }));
46
- return returnResult ? { error } : undefined;
46
+ return { error };
47
47
  }
48
48
  const newState = {
49
49
  error: undefined,
@@ -53,19 +53,18 @@ const query = (logTag, returnResult, store, cache, { updateQueryStateAndEntities
53
53
  : mergeResults
54
54
  ? mergeResults(
55
55
  // @ts-expect-error fix later
56
- (_a = cacheStateSelector(store.getState()).queries[queryKey][cacheKey]) === null || _a === void 0 ? void 0 : _a.result, response, params)
56
+ (_a = cacheStateSelector(store.getState()).queries[queryKey][cacheKey]) === null || _a === void 0 ? void 0 : _a.result, response, params, store)
57
57
  : response.result,
58
58
  };
59
59
  store.dispatch(updateQueryStateAndEntities(queryKey, cacheKey, newState, response));
60
- // @ts-expect-error fix types
61
- return returnResult
62
- ? {
63
- result: cacheResultSelector
64
- ? cacheResultSelector(cacheStateSelector(store.getState()),
65
- // @ts-expect-error fix types
66
- params)
67
- : newState === null || newState === void 0 ? void 0 : newState.result,
68
- }
69
- : undefined;
60
+ return {
61
+ // @ts-expect-error fix types
62
+ result: cacheResultSelector
63
+ ? cacheResultSelector(cacheStateSelector(store.getState()),
64
+ // @ts-expect-error fix types
65
+ params)
66
+ : newState === null || newState === void 0 ? void 0 : newState.result,
67
+ };
70
68
  });
71
69
  exports.query = query;
70
+ const CANCELLED_RESULT = Object.freeze({ cancelled: true });
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Store } from 'redux';
1
2
  import type { ReduxCacheState } from './reducer';
2
3
  export type Key = string | number | symbol;
3
4
  export type Dict<T> = Record<Key, T>;
@@ -73,8 +74,8 @@ export type QueryInfo<T extends Typenames, P, R, S> = {
73
74
  * Needed when query result may already be in the cache, e.g. for single entity query by id.
74
75
  * */
75
76
  resultSelector?: (state: S, params: P) => R | undefined;
76
- /** Merges results before saving to the store. */
77
- mergeResults?: (oldResult: R | undefined, response: QueryResponse<T, R>, params: P | undefined) => R;
77
+ /** Merges results before saving to the store. Default implementation is using the latest result. */
78
+ mergeResults?: (oldResult: R | undefined, response: QueryResponse<T, R>, params: P | undefined, store: Store) => R;
78
79
  /**
79
80
  * Cache key is used for storing the query state and for performing a fetch when it changes. Queries with the same cache key share their state.
80
81
  * Default implementation uses `JSON.stringify` or `String()` depending on type.
@@ -86,7 +87,7 @@ export type UseQueryOptions<T extends Typenames, QP, QR, MR, QK extends keyof (Q
86
87
  query: QK;
87
88
  params: QK extends keyof (QP | QR) ? QP[QK] : never;
88
89
  skip?: boolean;
89
- } & Pick<QK extends keyof (QP | QR) ? QueryInfo<T, QP[QK], QR[QK], ReduxCacheState<T, QR, MR>> : never, 'cachePolicy' | 'mergeResults' | 'getCacheKey'>;
90
+ } & Pick<QK extends keyof (QP | QR) ? QueryInfo<T, QP[QK], QR[QK], ReduxCacheState<T, QR, MR>> : never, 'cachePolicy'>;
90
91
  /**
91
92
  * @param cache-first for each params key fetch is not called if cache exists.
92
93
  * @param cache-and-fetch for each params key result is taken from cache and fetch is called.
@@ -120,7 +121,7 @@ export type MutationResult<R> = {
120
121
  export type QueryMutationState<R> = {
121
122
  /** `true` when query or mutation is currently in progress. */
122
123
  loading: boolean;
123
- /** Result of the latest successfull query response. */
124
+ /** Result of the latest successfull response. */
124
125
  result?: R;
125
126
  /** Error of the latest response. */
126
127
  error?: Error;
@@ -3,4 +3,4 @@ import { ActionMap } from './createActions';
3
3
  import { Cache, Key, QueryMutationState, Typenames } from './types';
4
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, MR>, "updateMutationStateAndEntities">, options: {
5
5
  mutation: MK;
6
- }, abortControllers: WeakMap<Store, Record<Key, AbortController>>) => readonly [(params: MK extends keyof MP & keyof MR ? MP[MK] : never) => Promise<void>, QueryMutationState<MK extends keyof MP & keyof MR ? MP[MK] : never>, () => boolean];
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>, () => boolean];
@@ -32,7 +32,7 @@ const useMutation = (cache, actions, options, abortControllers) => {
32
32
  },
33
33
  // mutate
34
34
  (params) => __awaiter(void 0, void 0, void 0, function* () {
35
- yield (0, mutate_1.mutate)('useMutation.mutate', false, store, cache, actions, mutationKey, params, abortControllers);
35
+ return yield (0, mutate_1.mutate)('useMutation.mutate', store, cache, actions, mutationKey, params, abortControllers);
36
36
  }),
37
37
  // abort
38
38
  () => {
@@ -1,3 +1,3 @@
1
1
  import { ActionMap } from './createActions';
2
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, QR, MR>, "updateQueryStateAndEntities">, options: UseQueryOptions<T, QP, QR, MR, QK>) => readonly [QueryMutationState<QK extends keyof QP & keyof QR ? QR[QK] : never>, () => Promise<void>];
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, QR, MR>, "updateQueryStateAndEntities">, options: UseQueryOptions<T, QP, QR, MR, QK>) => readonly [QueryMutationState<QK extends keyof QP & keyof QR ? QR[QK] : never>, () => 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,8 +16,9 @@ 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', getCacheKey = (_b = cache.queries[queryKey].getCacheKey) !== null && _b !== void 0 ? _b : (utilsAndConstants_1.defaultGetCacheKey), } = options;
19
+ const { query: queryKey, skip, params, cachePolicy = (_a = cache.queries[queryKey].cachePolicy) !== null && _a !== void 0 ? _a : 'cache-first', } = options;
20
20
  const logsEnabled = cache.options.logsEnabled;
21
+ const getCacheKey = (_b = cache.queries[queryKey].getCacheKey) !== null && _b !== void 0 ? _b : (utilsAndConstants_1.defaultGetCacheKey);
21
22
  const cacheResultSelector = cache.queries[queryKey].resultSelector;
22
23
  const cacheStateSelector = cache.cacheStateSelector;
23
24
  const store = (0, react_redux_1.useStore)();
@@ -34,7 +35,7 @@ const useQuery = (cache, actions, options) => {
34
35
  const resultFromSelector = (resultSelector && (0, react_redux_1.useSelector)(resultSelector));
35
36
  const hasResultFromSelector = resultFromSelector !== undefined;
36
37
  const fetch = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
37
- yield (0, query_1.query)('useQuery.fetch', false, store, cache, actions, queryKey, cacheKey, params);
38
+ return yield (0, query_1.query)('useQuery.fetch', store, cache, actions, queryKey, cacheKey, params);
38
39
  // eslint-disable-next-line react-hooks/exhaustive-deps
39
40
  }), [store, queryKey, cacheKey]);
40
41
  const queryStateFromSelector = (_c = (0, react_redux_1.useSelector)((state) => {
@@ -7,8 +7,4 @@ export declare const DEFAULT_QUERY_MUTATION_STATE: {
7
7
  };
8
8
  export declare const defaultGetCacheKey: <P = unknown>(params: P) => Key;
9
9
  export declare const log: (tag: string, data?: unknown) => void;
10
- /**
11
- * Apply changes to the entities map.
12
- * @return `undefined` if nothing to change, otherwise new entities map with applied changes.
13
- */
14
10
  export declare const applyEntityChanges: <T extends Typenames>(entities: EntitiesMap<T>, changes: EntityChanges<T>, options: CacheOptions) => EntitiesMap<T> | undefined;
@@ -28,10 +28,6 @@ const log = (tag, data) => {
28
28
  console.debug(`@${exports.PACKAGE_SHORT_NAME} [${tag}]`, data);
29
29
  };
30
30
  exports.log = log;
31
- /**
32
- * Apply changes to the entities map.
33
- * @return `undefined` if nothing to change, otherwise new entities map with applied changes.
34
- */
35
31
  const applyEntityChanges = (entities, changes, options) => {
36
32
  var _a, _b, _c;
37
33
  if (options.validateFunctionArguments) {
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.2.0",
5
+ "version": "0.4.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",