floppy-disk 2.0.2-beta.1 → 2.1.0-beta.1
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 +51 -30
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/preact/create-query.d.ts +4 -0
- package/esm/preact/create-query.js +15 -14
- package/esm/preact/create-stores.d.ts +2 -1
- package/esm/preact/create-stores.js +7 -7
- package/esm/react/create-query.d.ts +4 -0
- package/esm/react/create-query.js +15 -14
- package/esm/react/create-stores.d.ts +2 -1
- package/esm/react/create-stores.js +7 -7
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -0
- package/lib/preact/create-query.d.ts +4 -0
- package/lib/preact/create-query.js +14 -13
- package/lib/preact/create-stores.d.ts +2 -1
- package/lib/preact/create-stores.js +7 -7
- package/lib/react/create-query.d.ts +4 -0
- package/lib/react/create-query.js +14 -13
- package/lib/react/create-stores.d.ts +2 -1
- package/lib/react/create-stores.js +7 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight, simple, and powerful state management library.
|
|
4
4
|
|
|
5
|
-
This library was highly-inspired by [Zustand](https://www.npmjs.com/package/zustand) and [
|
|
5
|
+
This library was highly-inspired by [Zustand](https://www.npmjs.com/package/zustand) and [TanStack-Query](https://tanstack.com/query).
|
|
6
|
+
Both are awesome state manager. That's why this Floppy Disk library behaves like them, but with small DX improvement, **more power**, and **less bundle size**.
|
|
6
7
|
|
|
7
8
|
**Bundle Size Comparison:**
|
|
8
9
|
|
|
@@ -23,7 +24,7 @@ import { createQuery, createMutation } from 'floppy-disk'; // 8.2 kB (gzipped: 2
|
|
|
23
24
|
- Using Zustand & React-Query: https://demo-zustand-react-query.vercel.app/
|
|
24
25
|
👉 Total: **309.21 kB**
|
|
25
26
|
- Using Floppy Disk: https://demo-floppy-disk.vercel.app/
|
|
26
|
-
👉 Total: **270.
|
|
27
|
+
👉 Total: **270.87 kB** 🎉
|
|
27
28
|
|
|
28
29
|
## Key Features
|
|
29
30
|
|
|
@@ -50,6 +51,8 @@ import { createQuery, createMutation } from 'floppy-disk'; // 8.2 kB (gzipped: 2
|
|
|
50
51
|
- Can be used with literally any asynchronous data fetching client, including GraphQL ✅
|
|
51
52
|
- Create mutation ✅
|
|
52
53
|
|
|
54
|
+
**View official documentation on [floppy-disk.vercel.app](https://floppy-disk.vercel.app/)**
|
|
55
|
+
|
|
53
56
|
## Table of Contents
|
|
54
57
|
|
|
55
58
|
- [Key Features](#key-features)
|
|
@@ -60,6 +63,7 @@ import { createQuery, createMutation } from 'floppy-disk'; // 8.2 kB (gzipped: 2
|
|
|
60
63
|
- [Stores](#stores)
|
|
61
64
|
- [Query \& Mutation](#query--mutation)
|
|
62
65
|
- [Query State \& Network Fetching State](#query-state--network-fetching-state)
|
|
66
|
+
- [Inherited from createStores](#inherited-from-createstores)
|
|
63
67
|
- [Single Query](#single-query)
|
|
64
68
|
- [Single Query with Params](#single-query-with-params)
|
|
65
69
|
- [Paginated Query or Infinite Query](#paginated-query-or-infinite-query)
|
|
@@ -431,6 +435,35 @@ Here is the flow of the query data state:
|
|
|
431
435
|
For network fetching state, we use `isWaiting`.
|
|
432
436
|
The value will be `true` if the query is called and still waiting for the response.
|
|
433
437
|
|
|
438
|
+
### Inherited from createStores
|
|
439
|
+
|
|
440
|
+
The `createQuery` function inherits functionality from the `createStores` function, allowing us to perform the same result and actions available in `createStores`.
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
const useMyQuery = createQuery(myQueryFn, {
|
|
444
|
+
// 👇 Same as createStores options
|
|
445
|
+
defaultDeps: undefined,
|
|
446
|
+
onFirstSubscribe: (state) => console.log('onFirstSubscribe', state),
|
|
447
|
+
onSubscribe: (state) => console.log('onSubscribe', state),
|
|
448
|
+
onUnsubscribe: (state) => console.log('onUnsubscribe', state),
|
|
449
|
+
onLastUnsubscribe: (state) => console.log('onLastUnsubscribe', state),
|
|
450
|
+
onBeforeChangeKey: (nextKey, prevKey) => console.log('Store key changed', nextKey, prevKey),
|
|
451
|
+
|
|
452
|
+
// ... other createQuery options
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Custom reactivity (dependency array) also works:
|
|
457
|
+
|
|
458
|
+
```tsx
|
|
459
|
+
function QueryLoader() {
|
|
460
|
+
// This component doesn't care whether the query is success or error.
|
|
461
|
+
// It just listening to network fetching state. 👇
|
|
462
|
+
const { isWaiting } = useMyQuery((state) => [state.isWaiting]);
|
|
463
|
+
return <div>Is network fetching? {String(isWaiting)}</div>;
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
434
467
|
### Single Query
|
|
435
468
|
|
|
436
469
|
```jsx
|
|
@@ -458,22 +491,18 @@ function SingleQuery() {
|
|
|
458
491
|
|
|
459
492
|
> Example: [https://codesandbox.io/.../examples/react/query](https://codesandbox.io/p/sandbox/github/afiiif/floppy-disk/tree/main/examples/react/query)
|
|
460
493
|
|
|
461
|
-
Custom reactivity:
|
|
462
|
-
|
|
463
|
-
```jsx
|
|
464
|
-
// This component doesn't care whether the query is success or error.
|
|
465
|
-
// It just listening to network fetching state. 👇
|
|
466
|
-
function SingleQueryLoader() {
|
|
467
|
-
const { isWaiting } = useGitHubQuery((state) => [state.isWaiting]);
|
|
468
|
-
return <div>Is network fetching? {String(isWaiting)}</div>;
|
|
469
|
-
}
|
|
470
|
-
```
|
|
471
|
-
|
|
472
494
|
Actions:
|
|
473
495
|
|
|
496
|
+
Normally, we don't need reactivity for the actions.
|
|
497
|
+
Therefore, using `get` method will be better, since it will not re-render the component when a query state changed.
|
|
498
|
+
|
|
474
499
|
```jsx
|
|
475
500
|
function Actions() {
|
|
476
|
-
const { fetch, forceFetch, reset } = useGitHubQuery(
|
|
501
|
+
const { fetch, forceFetch, reset } = useGitHubQuery.get();
|
|
502
|
+
|
|
503
|
+
// Or like this:
|
|
504
|
+
// const { isLoading, data, error, fetch, forceFetch, reset } = useGitHubQuery();
|
|
505
|
+
|
|
477
506
|
return (
|
|
478
507
|
<>
|
|
479
508
|
<button onClick={fetch}>Call query if the query data is stale</button>
|
|
@@ -524,22 +553,14 @@ function MyComponent() {
|
|
|
524
553
|
Get data or do something outside component:
|
|
525
554
|
|
|
526
555
|
```jsx
|
|
527
|
-
const getData = () =>
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
function Actions() {
|
|
536
|
-
return (
|
|
537
|
-
<>
|
|
538
|
-
<button onClick={getData}>Get Data</button>
|
|
539
|
-
<button onClick={resetQuery}>Reset query</button>
|
|
540
|
-
</>
|
|
541
|
-
);
|
|
542
|
-
}
|
|
556
|
+
const getData = () => console.log(useGitHubQuery.get().data);
|
|
557
|
+
const resetQuery = () => useGitHubQuery.get().reset();
|
|
558
|
+
|
|
559
|
+
// Works just like createStores
|
|
560
|
+
useMyQuery.get(/* ... */);
|
|
561
|
+
useMyQuery.set(/* ... */);
|
|
562
|
+
useMyQuery.subscribe(/* ... */);
|
|
563
|
+
useMyQuery.getSubscribers(/* ... */);
|
|
543
564
|
```
|
|
544
565
|
|
|
545
566
|
### Single Query with Params
|
package/esm/index.d.ts
CHANGED
package/esm/index.js
CHANGED
|
@@ -4,6 +4,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
4
4
|
* Query store key, an object that will be hashed into a string as a query store identifier.
|
|
5
5
|
*/
|
|
6
6
|
key: TKey;
|
|
7
|
+
/**
|
|
8
|
+
* Query store key hash, a string used as a query store identifier.
|
|
9
|
+
*/
|
|
10
|
+
keyHash: string;
|
|
7
11
|
/**
|
|
8
12
|
* Will only be called if the data is stale or empty.
|
|
9
13
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState } from 'preact/hooks';
|
|
2
|
-
import {
|
|
2
|
+
import { identityFn, noop } from '../utils';
|
|
3
3
|
import { createStores } from './create-stores';
|
|
4
4
|
const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
5
5
|
if (value === true || (typeof value === 'function' && value(param) === true)) {
|
|
@@ -9,7 +9,6 @@ const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
|
9
9
|
ifAlways();
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
|
-
const DEFAULT_STALE_TIME = 3000; // 3 seconds
|
|
13
12
|
const INITIAL_QUERY_STATE = {
|
|
14
13
|
isWaiting: false,
|
|
15
14
|
isWaitingNextPage: false,
|
|
@@ -41,11 +40,13 @@ const useQueryDefaultDeps = (state) => [
|
|
|
41
40
|
state.hasNextPage,
|
|
42
41
|
];
|
|
43
42
|
export const createQuery = (queryFn, options = {}) => {
|
|
44
|
-
const { onFirstSubscribe = noop, onSubscribe = noop, onLastUnsubscribe = noop, onBeforeChangeKey = noop, defaultDeps = useQueryDefaultDeps, select = identityFn, staleTime =
|
|
43
|
+
const { onFirstSubscribe = noop, onSubscribe = noop, onLastUnsubscribe = noop, onBeforeChangeKey = noop, defaultDeps = useQueryDefaultDeps, select = identityFn, staleTime = 3000, // 3 seconds
|
|
44
|
+
fetchOnMount = true, fetchOnWindowFocus = true, enabled = true, retry = 1, retryDelay = 3000, // 3 seconds
|
|
45
|
+
keepPreviousData, getNextPageParam = () => undefined, onSuccess = noop, onError = noop, onSettled = noop, ...createStoresOptions } = options;
|
|
45
46
|
const retryTimeoutId = new Map();
|
|
46
47
|
const retryNextPageTimeoutId = new Map();
|
|
47
48
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
48
|
-
const useQuery = createStores(({ key: _key,
|
|
49
|
+
const useQuery = createStores(({ get, set, key: _key, keyHash }) => {
|
|
49
50
|
const key = _key;
|
|
50
51
|
const getRetryProps = (error, retryCount) => {
|
|
51
52
|
const maxRetryCount = (typeof retry === 'function' ? retry(error, key) : retry) || 0;
|
|
@@ -70,13 +71,13 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
70
71
|
set({ isGoingToRetry: false, isWaiting: true });
|
|
71
72
|
else
|
|
72
73
|
set({ isGoingToRetry: false, isWaiting: true, isRefetching: true });
|
|
73
|
-
clearTimeout(retryTimeoutId.get(
|
|
74
|
+
clearTimeout(retryTimeoutId.get(keyHash));
|
|
74
75
|
}
|
|
75
76
|
const stateBeforeCallQuery = { ...get(), pageParam };
|
|
76
|
-
preventReplaceResponse.set(
|
|
77
|
+
preventReplaceResponse.set(keyHash, false);
|
|
77
78
|
queryFn(key, stateBeforeCallQuery)
|
|
78
79
|
.then((response) => {
|
|
79
|
-
if (preventReplaceResponse.get(
|
|
80
|
+
if (preventReplaceResponse.get(keyHash)) {
|
|
80
81
|
set({ isWaiting: false });
|
|
81
82
|
return;
|
|
82
83
|
}
|
|
@@ -144,7 +145,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
144
145
|
hasNextPage: pageParam !== undefined,
|
|
145
146
|
});
|
|
146
147
|
if (shouldRetry) {
|
|
147
|
-
retryTimeoutId.set(
|
|
148
|
+
retryTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
148
149
|
set({ retryCount: prevState.retryCount + 1 });
|
|
149
150
|
callQuery();
|
|
150
151
|
}, delay));
|
|
@@ -172,7 +173,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
172
173
|
if (isWaitingNextPage || !hasNextPage)
|
|
173
174
|
return;
|
|
174
175
|
set({ isWaitingNextPage: true, isGoingToRetryNextPage: false });
|
|
175
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
176
|
+
clearTimeout(retryNextPageTimeoutId.get(keyHash));
|
|
176
177
|
queryFn(key, { ...state, pageParam })
|
|
177
178
|
.then((response) => {
|
|
178
179
|
const newPageParam = getNextPageParam(response, pageParams.length);
|
|
@@ -197,7 +198,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
197
198
|
isGoingToRetryNextPage: shouldRetry,
|
|
198
199
|
});
|
|
199
200
|
if (shouldRetry) {
|
|
200
|
-
retryNextPageTimeoutId.set(
|
|
201
|
+
retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
201
202
|
set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
|
|
202
203
|
fetchNextPage();
|
|
203
204
|
}, delay));
|
|
@@ -207,6 +208,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
207
208
|
return {
|
|
208
209
|
...INITIAL_QUERY_STATE,
|
|
209
210
|
key,
|
|
211
|
+
keyHash,
|
|
210
212
|
fetch,
|
|
211
213
|
forceFetch,
|
|
212
214
|
fetchNextPage,
|
|
@@ -225,7 +227,6 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
225
227
|
return {
|
|
226
228
|
...createStoresOptions,
|
|
227
229
|
defaultDeps,
|
|
228
|
-
hashKeyFn,
|
|
229
230
|
onFirstSubscribe: (state) => {
|
|
230
231
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
231
232
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
@@ -244,8 +245,8 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
244
245
|
window.removeEventListener('focus', fetchWindowFocusHandler);
|
|
245
246
|
}
|
|
246
247
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
247
|
-
clearTimeout(retryTimeoutId.get(
|
|
248
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
248
|
+
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
249
|
+
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
249
250
|
onLastUnsubscribe(state);
|
|
250
251
|
},
|
|
251
252
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
|
@@ -323,7 +324,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
323
324
|
response: optimisticResponse,
|
|
324
325
|
data: select(optimisticResponse, { key: key, data: null }),
|
|
325
326
|
});
|
|
326
|
-
preventReplaceResponse.set(
|
|
327
|
+
preventReplaceResponse.set(prevState.keyHash, true);
|
|
327
328
|
const revert = () => {
|
|
328
329
|
useQuery.set(key, {
|
|
329
330
|
isOptimisticData: false,
|
|
@@ -3,9 +3,10 @@ import { WatchProps } from './create-store';
|
|
|
3
3
|
type Maybe<T> = T | null | undefined;
|
|
4
4
|
export type StoreKey = Record<string, any> | undefined;
|
|
5
5
|
export type StoresInitializer<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = (api: {
|
|
6
|
-
key: TKey;
|
|
7
6
|
get: () => T;
|
|
8
7
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
8
|
+
key: TKey;
|
|
9
|
+
keyHash: string;
|
|
9
10
|
}) => T;
|
|
10
11
|
export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = {
|
|
11
12
|
/**
|
|
@@ -6,11 +6,11 @@ export const createStores = (initializer, options = {}) => {
|
|
|
6
6
|
const stores = new Map();
|
|
7
7
|
const getStore = (_key) => {
|
|
8
8
|
const key = _key || {};
|
|
9
|
-
const
|
|
10
|
-
if (!stores.has(
|
|
11
|
-
stores.set(
|
|
9
|
+
const keyHash = hashKeyFn(key);
|
|
10
|
+
if (!stores.has(keyHash)) {
|
|
11
|
+
stores.set(keyHash, initStore((api) => initializer({ key, keyHash, ...api }), options));
|
|
12
12
|
}
|
|
13
|
-
return stores.get(
|
|
13
|
+
return stores.get(keyHash);
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
* IMPORTANT NOTE: selectDeps function must not be changed after initialization.
|
|
@@ -18,9 +18,9 @@ export const createStores = (initializer, options = {}) => {
|
|
|
18
18
|
const useStores = (...args) => {
|
|
19
19
|
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
|
|
20
20
|
const key = _key || {};
|
|
21
|
-
const
|
|
21
|
+
const keyHash = hashKeyFn(key);
|
|
22
22
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
|
-
const { get, subscribe } = useMemo(() => getStore(key), [
|
|
23
|
+
const { get, subscribe } = useMemo(() => getStore(key), [keyHash]);
|
|
24
24
|
const [state, setState] = useState(get);
|
|
25
25
|
const isFirstRender = useRef(true);
|
|
26
26
|
const prevKey = useRef(key);
|
|
@@ -34,7 +34,7 @@ export const createStores = (initializer, options = {}) => {
|
|
|
34
34
|
const unsubs = subscribe(setState, selectDeps);
|
|
35
35
|
return unsubs;
|
|
36
36
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
|
-
}, [
|
|
37
|
+
}, [keyHash]);
|
|
38
38
|
return state;
|
|
39
39
|
};
|
|
40
40
|
useStores.get = (key) => {
|
|
@@ -4,6 +4,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
4
4
|
* Query store key, an object that will be hashed into a string as a query store identifier.
|
|
5
5
|
*/
|
|
6
6
|
key: TKey;
|
|
7
|
+
/**
|
|
8
|
+
* Query store key hash, a string used as a query store identifier.
|
|
9
|
+
*/
|
|
10
|
+
keyHash: string;
|
|
7
11
|
/**
|
|
8
12
|
* Will only be called if the data is stale or empty.
|
|
9
13
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { identityFn, noop } from '../utils';
|
|
3
3
|
import { createStores } from './create-stores';
|
|
4
4
|
const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
5
5
|
if (value === true || (typeof value === 'function' && value(param) === true)) {
|
|
@@ -9,7 +9,6 @@ const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
|
9
9
|
ifAlways();
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
|
-
const DEFAULT_STALE_TIME = 3000; // 3 seconds
|
|
13
12
|
const INITIAL_QUERY_STATE = {
|
|
14
13
|
isWaiting: false,
|
|
15
14
|
isWaitingNextPage: false,
|
|
@@ -41,11 +40,13 @@ const useQueryDefaultDeps = (state) => [
|
|
|
41
40
|
state.hasNextPage,
|
|
42
41
|
];
|
|
43
42
|
export const createQuery = (queryFn, options = {}) => {
|
|
44
|
-
const { onFirstSubscribe = noop, onSubscribe = noop, onLastUnsubscribe = noop, onBeforeChangeKey = noop, defaultDeps = useQueryDefaultDeps, select = identityFn, staleTime =
|
|
43
|
+
const { onFirstSubscribe = noop, onSubscribe = noop, onLastUnsubscribe = noop, onBeforeChangeKey = noop, defaultDeps = useQueryDefaultDeps, select = identityFn, staleTime = 3000, // 3 seconds
|
|
44
|
+
fetchOnMount = true, fetchOnWindowFocus = true, enabled = true, retry = 1, retryDelay = 3000, // 3 seconds
|
|
45
|
+
keepPreviousData, getNextPageParam = () => undefined, onSuccess = noop, onError = noop, onSettled = noop, ...createStoresOptions } = options;
|
|
45
46
|
const retryTimeoutId = new Map();
|
|
46
47
|
const retryNextPageTimeoutId = new Map();
|
|
47
48
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
48
|
-
const useQuery = createStores(({ key: _key,
|
|
49
|
+
const useQuery = createStores(({ get, set, key: _key, keyHash }) => {
|
|
49
50
|
const key = _key;
|
|
50
51
|
const getRetryProps = (error, retryCount) => {
|
|
51
52
|
const maxRetryCount = (typeof retry === 'function' ? retry(error, key) : retry) || 0;
|
|
@@ -70,13 +71,13 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
70
71
|
set({ isGoingToRetry: false, isWaiting: true });
|
|
71
72
|
else
|
|
72
73
|
set({ isGoingToRetry: false, isWaiting: true, isRefetching: true });
|
|
73
|
-
clearTimeout(retryTimeoutId.get(
|
|
74
|
+
clearTimeout(retryTimeoutId.get(keyHash));
|
|
74
75
|
}
|
|
75
76
|
const stateBeforeCallQuery = { ...get(), pageParam };
|
|
76
|
-
preventReplaceResponse.set(
|
|
77
|
+
preventReplaceResponse.set(keyHash, false);
|
|
77
78
|
queryFn(key, stateBeforeCallQuery)
|
|
78
79
|
.then((response) => {
|
|
79
|
-
if (preventReplaceResponse.get(
|
|
80
|
+
if (preventReplaceResponse.get(keyHash)) {
|
|
80
81
|
set({ isWaiting: false });
|
|
81
82
|
return;
|
|
82
83
|
}
|
|
@@ -144,7 +145,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
144
145
|
hasNextPage: pageParam !== undefined,
|
|
145
146
|
});
|
|
146
147
|
if (shouldRetry) {
|
|
147
|
-
retryTimeoutId.set(
|
|
148
|
+
retryTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
148
149
|
set({ retryCount: prevState.retryCount + 1 });
|
|
149
150
|
callQuery();
|
|
150
151
|
}, delay));
|
|
@@ -172,7 +173,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
172
173
|
if (isWaitingNextPage || !hasNextPage)
|
|
173
174
|
return;
|
|
174
175
|
set({ isWaitingNextPage: true, isGoingToRetryNextPage: false });
|
|
175
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
176
|
+
clearTimeout(retryNextPageTimeoutId.get(keyHash));
|
|
176
177
|
queryFn(key, { ...state, pageParam })
|
|
177
178
|
.then((response) => {
|
|
178
179
|
const newPageParam = getNextPageParam(response, pageParams.length);
|
|
@@ -197,7 +198,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
197
198
|
isGoingToRetryNextPage: shouldRetry,
|
|
198
199
|
});
|
|
199
200
|
if (shouldRetry) {
|
|
200
|
-
retryNextPageTimeoutId.set(
|
|
201
|
+
retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
201
202
|
set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
|
|
202
203
|
fetchNextPage();
|
|
203
204
|
}, delay));
|
|
@@ -207,6 +208,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
207
208
|
return {
|
|
208
209
|
...INITIAL_QUERY_STATE,
|
|
209
210
|
key,
|
|
211
|
+
keyHash,
|
|
210
212
|
fetch,
|
|
211
213
|
forceFetch,
|
|
212
214
|
fetchNextPage,
|
|
@@ -225,7 +227,6 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
225
227
|
return {
|
|
226
228
|
...createStoresOptions,
|
|
227
229
|
defaultDeps,
|
|
228
|
-
hashKeyFn,
|
|
229
230
|
onFirstSubscribe: (state) => {
|
|
230
231
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
231
232
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
@@ -244,8 +245,8 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
244
245
|
window.removeEventListener('focus', fetchWindowFocusHandler);
|
|
245
246
|
}
|
|
246
247
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
247
|
-
clearTimeout(retryTimeoutId.get(
|
|
248
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
248
|
+
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
249
|
+
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
249
250
|
onLastUnsubscribe(state);
|
|
250
251
|
},
|
|
251
252
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
|
@@ -323,7 +324,7 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
323
324
|
response: optimisticResponse,
|
|
324
325
|
data: select(optimisticResponse, { key: key, data: null }),
|
|
325
326
|
});
|
|
326
|
-
preventReplaceResponse.set(
|
|
327
|
+
preventReplaceResponse.set(prevState.keyHash, true);
|
|
327
328
|
const revert = () => {
|
|
328
329
|
useQuery.set(key, {
|
|
329
330
|
isOptimisticData: false,
|
|
@@ -3,9 +3,10 @@ import { WatchProps } from './create-store';
|
|
|
3
3
|
type Maybe<T> = T | null | undefined;
|
|
4
4
|
export type StoreKey = Record<string, any> | undefined;
|
|
5
5
|
export type StoresInitializer<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = (api: {
|
|
6
|
-
key: TKey;
|
|
7
6
|
get: () => T;
|
|
8
7
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
8
|
+
key: TKey;
|
|
9
|
+
keyHash: string;
|
|
9
10
|
}) => T;
|
|
10
11
|
export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = {
|
|
11
12
|
/**
|
|
@@ -6,11 +6,11 @@ export const createStores = (initializer, options = {}) => {
|
|
|
6
6
|
const stores = new Map();
|
|
7
7
|
const getStore = (_key) => {
|
|
8
8
|
const key = _key || {};
|
|
9
|
-
const
|
|
10
|
-
if (!stores.has(
|
|
11
|
-
stores.set(
|
|
9
|
+
const keyHash = hashKeyFn(key);
|
|
10
|
+
if (!stores.has(keyHash)) {
|
|
11
|
+
stores.set(keyHash, initStore((api) => initializer({ key, keyHash, ...api }), options));
|
|
12
12
|
}
|
|
13
|
-
return stores.get(
|
|
13
|
+
return stores.get(keyHash);
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
* IMPORTANT NOTE: selectDeps function must not be changed after initialization.
|
|
@@ -18,9 +18,9 @@ export const createStores = (initializer, options = {}) => {
|
|
|
18
18
|
const useStores = (...args) => {
|
|
19
19
|
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
|
|
20
20
|
const key = _key || {};
|
|
21
|
-
const
|
|
21
|
+
const keyHash = hashKeyFn(key);
|
|
22
22
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
23
|
-
const { get, subscribe } = useMemo(() => getStore(key), [
|
|
23
|
+
const { get, subscribe } = useMemo(() => getStore(key), [keyHash]);
|
|
24
24
|
const [state, setState] = useState(get);
|
|
25
25
|
const isFirstRender = useRef(true);
|
|
26
26
|
const prevKey = useRef(key);
|
|
@@ -34,7 +34,7 @@ export const createStores = (initializer, options = {}) => {
|
|
|
34
34
|
const unsubs = subscribe(setState, selectDeps);
|
|
35
35
|
return unsubs;
|
|
36
36
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
|
-
}, [
|
|
37
|
+
}, [keyHash]);
|
|
38
38
|
return state;
|
|
39
39
|
};
|
|
40
40
|
useStores.get = (key) => {
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashStoreKey = void 0;
|
|
3
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
var utils_1 = require("./utils");
|
|
6
|
+
Object.defineProperty(exports, "hashStoreKey", { enumerable: true, get: function () { return utils_1.hashStoreKey; } });
|
|
4
7
|
tslib_1.__exportStar(require("./vanilla"), exports);
|
|
5
8
|
tslib_1.__exportStar(require("./react"), exports);
|
|
@@ -4,6 +4,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
4
4
|
* Query store key, an object that will be hashed into a string as a query store identifier.
|
|
5
5
|
*/
|
|
6
6
|
key: TKey;
|
|
7
|
+
/**
|
|
8
|
+
* Query store key hash, a string used as a query store identifier.
|
|
9
|
+
*/
|
|
10
|
+
keyHash: string;
|
|
7
11
|
/**
|
|
8
12
|
* Will only be called if the data is stale or empty.
|
|
9
13
|
*/
|
|
@@ -12,7 +12,6 @@ const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
|
12
12
|
ifAlways();
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
-
const DEFAULT_STALE_TIME = 3000; // 3 seconds
|
|
16
15
|
const INITIAL_QUERY_STATE = {
|
|
17
16
|
isWaiting: false,
|
|
18
17
|
isWaitingNextPage: false,
|
|
@@ -44,11 +43,13 @@ const useQueryDefaultDeps = (state) => [
|
|
|
44
43
|
state.hasNextPage,
|
|
45
44
|
];
|
|
46
45
|
const createQuery = (queryFn, options = {}) => {
|
|
47
|
-
const { onFirstSubscribe = utils_1.noop, onSubscribe = utils_1.noop, onLastUnsubscribe = utils_1.noop, onBeforeChangeKey = utils_1.noop, defaultDeps = useQueryDefaultDeps, select = utils_1.identityFn, staleTime =
|
|
46
|
+
const { onFirstSubscribe = utils_1.noop, onSubscribe = utils_1.noop, onLastUnsubscribe = utils_1.noop, onBeforeChangeKey = utils_1.noop, defaultDeps = useQueryDefaultDeps, select = utils_1.identityFn, staleTime = 3000, // 3 seconds
|
|
47
|
+
fetchOnMount = true, fetchOnWindowFocus = true, enabled = true, retry = 1, retryDelay = 3000, // 3 seconds
|
|
48
|
+
keepPreviousData, getNextPageParam = () => undefined, onSuccess = utils_1.noop, onError = utils_1.noop, onSettled = utils_1.noop, ...createStoresOptions } = options;
|
|
48
49
|
const retryTimeoutId = new Map();
|
|
49
50
|
const retryNextPageTimeoutId = new Map();
|
|
50
51
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
51
|
-
const useQuery = (0, create_stores_1.createStores)(({ key: _key,
|
|
52
|
+
const useQuery = (0, create_stores_1.createStores)(({ get, set, key: _key, keyHash }) => {
|
|
52
53
|
const key = _key;
|
|
53
54
|
const getRetryProps = (error, retryCount) => {
|
|
54
55
|
const maxRetryCount = (typeof retry === 'function' ? retry(error, key) : retry) || 0;
|
|
@@ -73,13 +74,13 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
73
74
|
set({ isGoingToRetry: false, isWaiting: true });
|
|
74
75
|
else
|
|
75
76
|
set({ isGoingToRetry: false, isWaiting: true, isRefetching: true });
|
|
76
|
-
clearTimeout(retryTimeoutId.get(
|
|
77
|
+
clearTimeout(retryTimeoutId.get(keyHash));
|
|
77
78
|
}
|
|
78
79
|
const stateBeforeCallQuery = { ...get(), pageParam };
|
|
79
|
-
preventReplaceResponse.set(
|
|
80
|
+
preventReplaceResponse.set(keyHash, false);
|
|
80
81
|
queryFn(key, stateBeforeCallQuery)
|
|
81
82
|
.then((response) => {
|
|
82
|
-
if (preventReplaceResponse.get(
|
|
83
|
+
if (preventReplaceResponse.get(keyHash)) {
|
|
83
84
|
set({ isWaiting: false });
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
@@ -147,7 +148,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
147
148
|
hasNextPage: pageParam !== undefined,
|
|
148
149
|
});
|
|
149
150
|
if (shouldRetry) {
|
|
150
|
-
retryTimeoutId.set(
|
|
151
|
+
retryTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
151
152
|
set({ retryCount: prevState.retryCount + 1 });
|
|
152
153
|
callQuery();
|
|
153
154
|
}, delay));
|
|
@@ -175,7 +176,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
175
176
|
if (isWaitingNextPage || !hasNextPage)
|
|
176
177
|
return;
|
|
177
178
|
set({ isWaitingNextPage: true, isGoingToRetryNextPage: false });
|
|
178
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
179
|
+
clearTimeout(retryNextPageTimeoutId.get(keyHash));
|
|
179
180
|
queryFn(key, { ...state, pageParam })
|
|
180
181
|
.then((response) => {
|
|
181
182
|
const newPageParam = getNextPageParam(response, pageParams.length);
|
|
@@ -200,7 +201,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
200
201
|
isGoingToRetryNextPage: shouldRetry,
|
|
201
202
|
});
|
|
202
203
|
if (shouldRetry) {
|
|
203
|
-
retryNextPageTimeoutId.set(
|
|
204
|
+
retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
204
205
|
set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
|
|
205
206
|
fetchNextPage();
|
|
206
207
|
}, delay));
|
|
@@ -210,6 +211,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
210
211
|
return {
|
|
211
212
|
...INITIAL_QUERY_STATE,
|
|
212
213
|
key,
|
|
214
|
+
keyHash,
|
|
213
215
|
fetch,
|
|
214
216
|
forceFetch,
|
|
215
217
|
fetchNextPage,
|
|
@@ -228,7 +230,6 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
228
230
|
return {
|
|
229
231
|
...createStoresOptions,
|
|
230
232
|
defaultDeps,
|
|
231
|
-
hashKeyFn,
|
|
232
233
|
onFirstSubscribe: (state) => {
|
|
233
234
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
234
235
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
@@ -247,8 +248,8 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
247
248
|
window.removeEventListener('focus', fetchWindowFocusHandler);
|
|
248
249
|
}
|
|
249
250
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
250
|
-
clearTimeout(retryTimeoutId.get(
|
|
251
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
251
|
+
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
252
|
+
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
252
253
|
onLastUnsubscribe(state);
|
|
253
254
|
},
|
|
254
255
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
|
@@ -326,7 +327,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
326
327
|
response: optimisticResponse,
|
|
327
328
|
data: select(optimisticResponse, { key: key, data: null }),
|
|
328
329
|
});
|
|
329
|
-
preventReplaceResponse.set(
|
|
330
|
+
preventReplaceResponse.set(prevState.keyHash, true);
|
|
330
331
|
const revert = () => {
|
|
331
332
|
useQuery.set(key, {
|
|
332
333
|
isOptimisticData: false,
|
|
@@ -3,9 +3,10 @@ import { WatchProps } from './create-store';
|
|
|
3
3
|
type Maybe<T> = T | null | undefined;
|
|
4
4
|
export type StoreKey = Record<string, any> | undefined;
|
|
5
5
|
export type StoresInitializer<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = (api: {
|
|
6
|
-
key: TKey;
|
|
7
6
|
get: () => T;
|
|
8
7
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
8
|
+
key: TKey;
|
|
9
|
+
keyHash: string;
|
|
9
10
|
}) => T;
|
|
10
11
|
export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = {
|
|
11
12
|
/**
|
|
@@ -9,11 +9,11 @@ const createStores = (initializer, options = {}) => {
|
|
|
9
9
|
const stores = new Map();
|
|
10
10
|
const getStore = (_key) => {
|
|
11
11
|
const key = _key || {};
|
|
12
|
-
const
|
|
13
|
-
if (!stores.has(
|
|
14
|
-
stores.set(
|
|
12
|
+
const keyHash = hashKeyFn(key);
|
|
13
|
+
if (!stores.has(keyHash)) {
|
|
14
|
+
stores.set(keyHash, (0, vanilla_1.initStore)((api) => initializer({ key, keyHash, ...api }), options));
|
|
15
15
|
}
|
|
16
|
-
return stores.get(
|
|
16
|
+
return stores.get(keyHash);
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* IMPORTANT NOTE: selectDeps function must not be changed after initialization.
|
|
@@ -21,9 +21,9 @@ const createStores = (initializer, options = {}) => {
|
|
|
21
21
|
const useStores = (...args) => {
|
|
22
22
|
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
|
|
23
23
|
const key = _key || {};
|
|
24
|
-
const
|
|
24
|
+
const keyHash = hashKeyFn(key);
|
|
25
25
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
|
-
const { get, subscribe } = (0, hooks_1.useMemo)(() => getStore(key), [
|
|
26
|
+
const { get, subscribe } = (0, hooks_1.useMemo)(() => getStore(key), [keyHash]);
|
|
27
27
|
const [state, setState] = (0, hooks_1.useState)(get);
|
|
28
28
|
const isFirstRender = (0, hooks_1.useRef)(true);
|
|
29
29
|
const prevKey = (0, hooks_1.useRef)(key);
|
|
@@ -37,7 +37,7 @@ const createStores = (initializer, options = {}) => {
|
|
|
37
37
|
const unsubs = subscribe(setState, selectDeps);
|
|
38
38
|
return unsubs;
|
|
39
39
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
-
}, [
|
|
40
|
+
}, [keyHash]);
|
|
41
41
|
return state;
|
|
42
42
|
};
|
|
43
43
|
useStores.get = (key) => {
|
|
@@ -4,6 +4,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
4
4
|
* Query store key, an object that will be hashed into a string as a query store identifier.
|
|
5
5
|
*/
|
|
6
6
|
key: TKey;
|
|
7
|
+
/**
|
|
8
|
+
* Query store key hash, a string used as a query store identifier.
|
|
9
|
+
*/
|
|
10
|
+
keyHash: string;
|
|
7
11
|
/**
|
|
8
12
|
* Will only be called if the data is stale or empty.
|
|
9
13
|
*/
|
|
@@ -12,7 +12,6 @@ const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
|
12
12
|
ifAlways();
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
-
const DEFAULT_STALE_TIME = 3000; // 3 seconds
|
|
16
15
|
const INITIAL_QUERY_STATE = {
|
|
17
16
|
isWaiting: false,
|
|
18
17
|
isWaitingNextPage: false,
|
|
@@ -44,11 +43,13 @@ const useQueryDefaultDeps = (state) => [
|
|
|
44
43
|
state.hasNextPage,
|
|
45
44
|
];
|
|
46
45
|
const createQuery = (queryFn, options = {}) => {
|
|
47
|
-
const { onFirstSubscribe = utils_1.noop, onSubscribe = utils_1.noop, onLastUnsubscribe = utils_1.noop, onBeforeChangeKey = utils_1.noop, defaultDeps = useQueryDefaultDeps, select = utils_1.identityFn, staleTime =
|
|
46
|
+
const { onFirstSubscribe = utils_1.noop, onSubscribe = utils_1.noop, onLastUnsubscribe = utils_1.noop, onBeforeChangeKey = utils_1.noop, defaultDeps = useQueryDefaultDeps, select = utils_1.identityFn, staleTime = 3000, // 3 seconds
|
|
47
|
+
fetchOnMount = true, fetchOnWindowFocus = true, enabled = true, retry = 1, retryDelay = 3000, // 3 seconds
|
|
48
|
+
keepPreviousData, getNextPageParam = () => undefined, onSuccess = utils_1.noop, onError = utils_1.noop, onSettled = utils_1.noop, ...createStoresOptions } = options;
|
|
48
49
|
const retryTimeoutId = new Map();
|
|
49
50
|
const retryNextPageTimeoutId = new Map();
|
|
50
51
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
51
|
-
const useQuery = (0, create_stores_1.createStores)(({ key: _key,
|
|
52
|
+
const useQuery = (0, create_stores_1.createStores)(({ get, set, key: _key, keyHash }) => {
|
|
52
53
|
const key = _key;
|
|
53
54
|
const getRetryProps = (error, retryCount) => {
|
|
54
55
|
const maxRetryCount = (typeof retry === 'function' ? retry(error, key) : retry) || 0;
|
|
@@ -73,13 +74,13 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
73
74
|
set({ isGoingToRetry: false, isWaiting: true });
|
|
74
75
|
else
|
|
75
76
|
set({ isGoingToRetry: false, isWaiting: true, isRefetching: true });
|
|
76
|
-
clearTimeout(retryTimeoutId.get(
|
|
77
|
+
clearTimeout(retryTimeoutId.get(keyHash));
|
|
77
78
|
}
|
|
78
79
|
const stateBeforeCallQuery = { ...get(), pageParam };
|
|
79
|
-
preventReplaceResponse.set(
|
|
80
|
+
preventReplaceResponse.set(keyHash, false);
|
|
80
81
|
queryFn(key, stateBeforeCallQuery)
|
|
81
82
|
.then((response) => {
|
|
82
|
-
if (preventReplaceResponse.get(
|
|
83
|
+
if (preventReplaceResponse.get(keyHash)) {
|
|
83
84
|
set({ isWaiting: false });
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
@@ -147,7 +148,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
147
148
|
hasNextPage: pageParam !== undefined,
|
|
148
149
|
});
|
|
149
150
|
if (shouldRetry) {
|
|
150
|
-
retryTimeoutId.set(
|
|
151
|
+
retryTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
151
152
|
set({ retryCount: prevState.retryCount + 1 });
|
|
152
153
|
callQuery();
|
|
153
154
|
}, delay));
|
|
@@ -175,7 +176,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
175
176
|
if (isWaitingNextPage || !hasNextPage)
|
|
176
177
|
return;
|
|
177
178
|
set({ isWaitingNextPage: true, isGoingToRetryNextPage: false });
|
|
178
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
179
|
+
clearTimeout(retryNextPageTimeoutId.get(keyHash));
|
|
179
180
|
queryFn(key, { ...state, pageParam })
|
|
180
181
|
.then((response) => {
|
|
181
182
|
const newPageParam = getNextPageParam(response, pageParams.length);
|
|
@@ -200,7 +201,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
200
201
|
isGoingToRetryNextPage: shouldRetry,
|
|
201
202
|
});
|
|
202
203
|
if (shouldRetry) {
|
|
203
|
-
retryNextPageTimeoutId.set(
|
|
204
|
+
retryNextPageTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
204
205
|
set({ retryNextPageCount: prevState.retryNextPageCount + 1 });
|
|
205
206
|
fetchNextPage();
|
|
206
207
|
}, delay));
|
|
@@ -210,6 +211,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
210
211
|
return {
|
|
211
212
|
...INITIAL_QUERY_STATE,
|
|
212
213
|
key,
|
|
214
|
+
keyHash,
|
|
213
215
|
fetch,
|
|
214
216
|
forceFetch,
|
|
215
217
|
fetchNextPage,
|
|
@@ -228,7 +230,6 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
228
230
|
return {
|
|
229
231
|
...createStoresOptions,
|
|
230
232
|
defaultDeps,
|
|
231
|
-
hashKeyFn,
|
|
232
233
|
onFirstSubscribe: (state) => {
|
|
233
234
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
234
235
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
@@ -247,8 +248,8 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
247
248
|
window.removeEventListener('focus', fetchWindowFocusHandler);
|
|
248
249
|
}
|
|
249
250
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
250
|
-
clearTimeout(retryTimeoutId.get(
|
|
251
|
-
clearTimeout(retryNextPageTimeoutId.get(
|
|
251
|
+
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
252
|
+
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
252
253
|
onLastUnsubscribe(state);
|
|
253
254
|
},
|
|
254
255
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
|
@@ -326,7 +327,7 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
326
327
|
response: optimisticResponse,
|
|
327
328
|
data: select(optimisticResponse, { key: key, data: null }),
|
|
328
329
|
});
|
|
329
|
-
preventReplaceResponse.set(
|
|
330
|
+
preventReplaceResponse.set(prevState.keyHash, true);
|
|
330
331
|
const revert = () => {
|
|
331
332
|
useQuery.set(key, {
|
|
332
333
|
isOptimisticData: false,
|
|
@@ -3,9 +3,10 @@ import { WatchProps } from './create-store';
|
|
|
3
3
|
type Maybe<T> = T | null | undefined;
|
|
4
4
|
export type StoreKey = Record<string, any> | undefined;
|
|
5
5
|
export type StoresInitializer<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = (api: {
|
|
6
|
-
key: TKey;
|
|
7
6
|
get: () => T;
|
|
8
7
|
set: (value: SetStoreData<T>, silent?: boolean) => void;
|
|
8
|
+
key: TKey;
|
|
9
|
+
keyHash: string;
|
|
9
10
|
}) => T;
|
|
10
11
|
export type UseStores<TKey extends StoreKey = StoreKey, T extends StoreData = StoreData> = {
|
|
11
12
|
/**
|
|
@@ -9,11 +9,11 @@ const createStores = (initializer, options = {}) => {
|
|
|
9
9
|
const stores = new Map();
|
|
10
10
|
const getStore = (_key) => {
|
|
11
11
|
const key = _key || {};
|
|
12
|
-
const
|
|
13
|
-
if (!stores.has(
|
|
14
|
-
stores.set(
|
|
12
|
+
const keyHash = hashKeyFn(key);
|
|
13
|
+
if (!stores.has(keyHash)) {
|
|
14
|
+
stores.set(keyHash, (0, vanilla_1.initStore)((api) => initializer({ key, keyHash, ...api }), options));
|
|
15
15
|
}
|
|
16
|
-
return stores.get(
|
|
16
|
+
return stores.get(keyHash);
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* IMPORTANT NOTE: selectDeps function must not be changed after initialization.
|
|
@@ -21,9 +21,9 @@ const createStores = (initializer, options = {}) => {
|
|
|
21
21
|
const useStores = (...args) => {
|
|
22
22
|
const [_key, selectDeps = defaultDeps] = (typeof args[0] === 'function' ? [{}, args[0]] : args);
|
|
23
23
|
const key = _key || {};
|
|
24
|
-
const
|
|
24
|
+
const keyHash = hashKeyFn(key);
|
|
25
25
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
|
-
const { get, subscribe } = (0, react_1.useMemo)(() => getStore(key), [
|
|
26
|
+
const { get, subscribe } = (0, react_1.useMemo)(() => getStore(key), [keyHash]);
|
|
27
27
|
const [state, setState] = (0, react_1.useState)(get);
|
|
28
28
|
const isFirstRender = (0, react_1.useRef)(true);
|
|
29
29
|
const prevKey = (0, react_1.useRef)(key);
|
|
@@ -37,7 +37,7 @@ const createStores = (initializer, options = {}) => {
|
|
|
37
37
|
const unsubs = subscribe(setState, selectDeps);
|
|
38
38
|
return unsubs;
|
|
39
39
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
-
}, [
|
|
40
|
+
}, [keyHash]);
|
|
41
41
|
return state;
|
|
42
42
|
};
|
|
43
43
|
useStores.get = (key) => {
|