floppy-disk 2.3.1 → 2.4.0-beta.2
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/esm/preact/create-query.d.ts +21 -1
- package/esm/preact/create-query.js +50 -25
- package/esm/react/create-query.d.ts +21 -1
- package/esm/react/create-query.js +50 -25
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.js +6 -0
- package/lib/preact/create-query.d.ts +21 -1
- package/lib/preact/create-query.js +49 -24
- package/lib/react/create-query.d.ts +21 -1
- package/lib/react/create-query.js +49 -24
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +8 -1
- package/package.json +1 -1
|
@@ -72,7 +72,7 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
72
72
|
*
|
|
73
73
|
* `"success"` = has data.
|
|
74
74
|
*
|
|
75
|
-
* `"error"` = has error.
|
|
75
|
+
* `"error"` = has error and no data.
|
|
76
76
|
*
|
|
77
77
|
* It has no relation with network fetching state.
|
|
78
78
|
* If you're looking for network fetching state, use `isWaiting` instead.
|
|
@@ -178,9 +178,29 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
|
|
|
178
178
|
* This function should return a variable that will be used when fetching next page (`pageParam`).
|
|
179
179
|
*/
|
|
180
180
|
getNextPageParam?: (lastPage: TResponse, index: number) => any;
|
|
181
|
+
onBeforeFetch?: (cancel: () => void, state: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
181
182
|
onSuccess?: (response: TResponse, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
182
183
|
onError?: (error: TError, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
183
184
|
onSettled?: (stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
185
|
+
/**
|
|
186
|
+
* Cache time in miliseconds.
|
|
187
|
+
*
|
|
188
|
+
* When a query becomes inactive (no longer have subscribers), it will be reset after this duration,
|
|
189
|
+
* and the cache data will be garbage collected.
|
|
190
|
+
*
|
|
191
|
+
* Set it to `Infinity` to disable garbage collection.
|
|
192
|
+
*
|
|
193
|
+
* Defaults to `5 * 60 * 1000` (5 minutes).
|
|
194
|
+
*/
|
|
195
|
+
cacheTime?: number;
|
|
196
|
+
/**
|
|
197
|
+
* Polling interval in milliseconds.
|
|
198
|
+
*
|
|
199
|
+
* Disabled by default.
|
|
200
|
+
*
|
|
201
|
+
* If the query is on error state, the polling interval will be disabled, and it will use `retry` instead.
|
|
202
|
+
*/
|
|
203
|
+
refetchInterval?: number | false | (() => number | false);
|
|
184
204
|
};
|
|
185
205
|
export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
|
|
186
206
|
/**
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { h as createElement } from 'preact';
|
|
2
2
|
import { useState } from 'preact/hooks';
|
|
3
|
-
import { hasValue, identityFn, noop } from '../utils';
|
|
3
|
+
import { getValueOrComputedValue, hasValue, identityFn, noop } from '../utils';
|
|
4
4
|
import { createStores } from './create-stores';
|
|
5
|
-
const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
6
|
-
if (value === true || (typeof value === 'function' && value(param) === true)) {
|
|
7
|
-
ifTrue();
|
|
8
|
-
}
|
|
9
|
-
else if (value === 'always' || (typeof value === 'function' && value(param) === 'always')) {
|
|
10
|
-
ifAlways();
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
5
|
const INITIAL_QUERY_STATE = {
|
|
14
6
|
isWaiting: false,
|
|
15
7
|
isWaitingNextPage: false,
|
|
@@ -44,26 +36,36 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
44
36
|
const defaultFetchOnWindowFocus = options.fetchOnMount ?? true;
|
|
45
37
|
const { onFirstSubscribe = noop, onSubscribe = noop, onLastUnsubscribe = noop, onBeforeChangeKey = noop, defaultDeps = useQueryDefaultDeps, select = identityFn, staleTime = 3000, // 3 seconds
|
|
46
38
|
fetchOnMount = true, fetchOnWindowFocus = defaultFetchOnWindowFocus, enabled = true, retry = 1, retryDelay = 2000, // 2 seconds
|
|
47
|
-
keepPreviousData, getNextPageParam = () => undefined, onSuccess = noop, onError = noop, onSettled = noop, ...createStoresOptions } = options;
|
|
39
|
+
keepPreviousData, getNextPageParam = () => undefined, onBeforeFetch = noop, onSuccess = noop, onError = noop, onSettled = noop, cacheTime = 5 * 60 * 1000, refetchInterval = false, ...createStoresOptions } = options;
|
|
48
40
|
const retryTimeoutId = new Map();
|
|
49
41
|
const retryNextPageTimeoutId = new Map();
|
|
42
|
+
const resetTimeoutId = new Map();
|
|
43
|
+
const refetchIntervalTimeoutId = new Map();
|
|
50
44
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
51
45
|
const useQuery = createStores(({ get, set, key: _key, keyHash }) => {
|
|
52
46
|
const key = _key;
|
|
53
47
|
const getRetryProps = (error, retryCount) => {
|
|
54
48
|
const prevState = get();
|
|
55
|
-
const maxRetryCount = (
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
return { shouldRetry, delay };
|
|
49
|
+
const maxRetryCount = getValueOrComputedValue(retry, error, prevState) || 0;
|
|
50
|
+
const delay = getValueOrComputedValue(retryDelay, error, prevState) || 0;
|
|
51
|
+
return { shouldRetry: retryCount < maxRetryCount, delay };
|
|
59
52
|
};
|
|
60
53
|
const forceFetch = () => new Promise((resolve) => {
|
|
61
54
|
const responseAllPages = [];
|
|
62
55
|
const newPageParams = [undefined];
|
|
63
56
|
let pageParam = undefined;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
clearTimeout(refetchIntervalTimeoutId.get(keyHash));
|
|
58
|
+
const state = get();
|
|
59
|
+
const { isWaiting, isLoading, pageParams } = state;
|
|
60
|
+
if (isWaiting || !getValueOrComputedValue(enabled, key))
|
|
61
|
+
return resolve(state);
|
|
62
|
+
let shouldcancel = false;
|
|
63
|
+
const cancel = () => {
|
|
64
|
+
shouldcancel = true;
|
|
65
|
+
};
|
|
66
|
+
onBeforeFetch(cancel, state);
|
|
67
|
+
if (shouldcancel)
|
|
68
|
+
return resolve(state);
|
|
67
69
|
if (isLoading)
|
|
68
70
|
set({ isWaiting: true });
|
|
69
71
|
else
|
|
@@ -114,6 +116,12 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
114
116
|
pageParams: newPageParams,
|
|
115
117
|
hasNextPage: hasValue(newPageParam),
|
|
116
118
|
});
|
|
119
|
+
const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval);
|
|
120
|
+
if (refetchIntervalValue) {
|
|
121
|
+
refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
122
|
+
forceFetch();
|
|
123
|
+
}, refetchIntervalValue));
|
|
124
|
+
}
|
|
117
125
|
onSuccess(response, stateBeforeCallQuery);
|
|
118
126
|
resolve(get());
|
|
119
127
|
})
|
|
@@ -227,26 +235,37 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
227
235
|
}, (() => {
|
|
228
236
|
const fetchWindowFocusHandler = () => {
|
|
229
237
|
useQuery.getAllWithSubscriber().forEach((state) => {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
const result = getValueOrComputedValue(fetchOnWindowFocus, state.key);
|
|
239
|
+
if (result === 'always')
|
|
240
|
+
state.forceFetch();
|
|
241
|
+
else if (result)
|
|
242
|
+
state.fetch();
|
|
234
243
|
});
|
|
235
244
|
};
|
|
236
245
|
return {
|
|
237
246
|
...createStoresOptions,
|
|
238
247
|
defaultDeps,
|
|
239
248
|
onFirstSubscribe: (state) => {
|
|
249
|
+
if (state.isSuccess) {
|
|
250
|
+
const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval);
|
|
251
|
+
if (refetchIntervalValue) {
|
|
252
|
+
refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
253
|
+
state.forceFetch();
|
|
254
|
+
}, refetchIntervalValue));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
240
257
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
241
258
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
242
259
|
}
|
|
260
|
+
clearTimeout(resetTimeoutId.get(state.keyHash));
|
|
243
261
|
onFirstSubscribe(state);
|
|
244
262
|
},
|
|
245
263
|
onSubscribe: (state) => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
264
|
+
const result = getValueOrComputedValue(fetchOnMount, state.key);
|
|
265
|
+
if (result === 'always')
|
|
266
|
+
state.forceFetch();
|
|
267
|
+
else if (result)
|
|
268
|
+
state.fetch();
|
|
250
269
|
onSubscribe(state);
|
|
251
270
|
},
|
|
252
271
|
onLastUnsubscribe: (state) => {
|
|
@@ -256,6 +275,12 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
256
275
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
257
276
|
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
258
277
|
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
278
|
+
clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
|
|
279
|
+
if (typeof window !== 'undefined' && cacheTime !== Infinity) {
|
|
280
|
+
resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
281
|
+
useQuery.set(state.key, INITIAL_QUERY_STATE);
|
|
282
|
+
}, cacheTime));
|
|
283
|
+
}
|
|
259
284
|
onLastUnsubscribe(state);
|
|
260
285
|
},
|
|
261
286
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
|
@@ -71,7 +71,7 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
71
71
|
*
|
|
72
72
|
* `"success"` = has data.
|
|
73
73
|
*
|
|
74
|
-
* `"error"` = has error.
|
|
74
|
+
* `"error"` = has error and no data.
|
|
75
75
|
*
|
|
76
76
|
* It has no relation with network fetching state.
|
|
77
77
|
* If you're looking for network fetching state, use `isWaiting` instead.
|
|
@@ -177,9 +177,29 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
|
|
|
177
177
|
* This function should return a variable that will be used when fetching next page (`pageParam`).
|
|
178
178
|
*/
|
|
179
179
|
getNextPageParam?: (lastPage: TResponse, index: number) => any;
|
|
180
|
+
onBeforeFetch?: (cancel: () => void, state: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
180
181
|
onSuccess?: (response: TResponse, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
181
182
|
onError?: (error: TError, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
182
183
|
onSettled?: (stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
184
|
+
/**
|
|
185
|
+
* Cache time in miliseconds.
|
|
186
|
+
*
|
|
187
|
+
* When a query becomes inactive (no longer have subscribers), it will be reset after this duration,
|
|
188
|
+
* and the cache data will be garbage collected.
|
|
189
|
+
*
|
|
190
|
+
* Set it to `Infinity` to disable garbage collection.
|
|
191
|
+
*
|
|
192
|
+
* Defaults to `5 * 60 * 1000` (5 minutes).
|
|
193
|
+
*/
|
|
194
|
+
cacheTime?: number;
|
|
195
|
+
/**
|
|
196
|
+
* Polling interval in milliseconds.
|
|
197
|
+
*
|
|
198
|
+
* Disabled by default.
|
|
199
|
+
*
|
|
200
|
+
* If the query is on error state, the polling interval will be disabled, and it will use `retry` instead.
|
|
201
|
+
*/
|
|
202
|
+
refetchInterval?: number | false | (() => number | false);
|
|
183
203
|
};
|
|
184
204
|
export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
|
|
185
205
|
/**
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { createElement, useState } from 'react';
|
|
2
|
-
import { hasValue, identityFn, noop } from '../utils';
|
|
2
|
+
import { getValueOrComputedValue, hasValue, identityFn, noop } from '../utils';
|
|
3
3
|
import { createStores } from './create-stores';
|
|
4
|
-
const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
5
|
-
if (value === true || (typeof value === 'function' && value(param) === true)) {
|
|
6
|
-
ifTrue();
|
|
7
|
-
}
|
|
8
|
-
else if (value === 'always' || (typeof value === 'function' && value(param) === 'always')) {
|
|
9
|
-
ifAlways();
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
4
|
const INITIAL_QUERY_STATE = {
|
|
13
5
|
isWaiting: false,
|
|
14
6
|
isWaitingNextPage: false,
|
|
@@ -43,26 +35,36 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
43
35
|
const defaultFetchOnWindowFocus = options.fetchOnMount ?? true;
|
|
44
36
|
const { onFirstSubscribe = noop, onSubscribe = noop, onLastUnsubscribe = noop, onBeforeChangeKey = noop, defaultDeps = useQueryDefaultDeps, select = identityFn, staleTime = 3000, // 3 seconds
|
|
45
37
|
fetchOnMount = true, fetchOnWindowFocus = defaultFetchOnWindowFocus, enabled = true, retry = 1, retryDelay = 2000, // 2 seconds
|
|
46
|
-
keepPreviousData, getNextPageParam = () => undefined, onSuccess = noop, onError = noop, onSettled = noop, ...createStoresOptions } = options;
|
|
38
|
+
keepPreviousData, getNextPageParam = () => undefined, onBeforeFetch = noop, onSuccess = noop, onError = noop, onSettled = noop, cacheTime = 5 * 60 * 1000, refetchInterval = false, ...createStoresOptions } = options;
|
|
47
39
|
const retryTimeoutId = new Map();
|
|
48
40
|
const retryNextPageTimeoutId = new Map();
|
|
41
|
+
const resetTimeoutId = new Map();
|
|
42
|
+
const refetchIntervalTimeoutId = new Map();
|
|
49
43
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
50
44
|
const useQuery = createStores(({ get, set, key: _key, keyHash }) => {
|
|
51
45
|
const key = _key;
|
|
52
46
|
const getRetryProps = (error, retryCount) => {
|
|
53
47
|
const prevState = get();
|
|
54
|
-
const maxRetryCount = (
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
return { shouldRetry, delay };
|
|
48
|
+
const maxRetryCount = getValueOrComputedValue(retry, error, prevState) || 0;
|
|
49
|
+
const delay = getValueOrComputedValue(retryDelay, error, prevState) || 0;
|
|
50
|
+
return { shouldRetry: retryCount < maxRetryCount, delay };
|
|
58
51
|
};
|
|
59
52
|
const forceFetch = () => new Promise((resolve) => {
|
|
60
53
|
const responseAllPages = [];
|
|
61
54
|
const newPageParams = [undefined];
|
|
62
55
|
let pageParam = undefined;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
clearTimeout(refetchIntervalTimeoutId.get(keyHash));
|
|
57
|
+
const state = get();
|
|
58
|
+
const { isWaiting, isLoading, pageParams } = state;
|
|
59
|
+
if (isWaiting || !getValueOrComputedValue(enabled, key))
|
|
60
|
+
return resolve(state);
|
|
61
|
+
let shouldcancel = false;
|
|
62
|
+
const cancel = () => {
|
|
63
|
+
shouldcancel = true;
|
|
64
|
+
};
|
|
65
|
+
onBeforeFetch(cancel, state);
|
|
66
|
+
if (shouldcancel)
|
|
67
|
+
return resolve(state);
|
|
66
68
|
if (isLoading)
|
|
67
69
|
set({ isWaiting: true });
|
|
68
70
|
else
|
|
@@ -113,6 +115,12 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
113
115
|
pageParams: newPageParams,
|
|
114
116
|
hasNextPage: hasValue(newPageParam),
|
|
115
117
|
});
|
|
118
|
+
const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval);
|
|
119
|
+
if (refetchIntervalValue) {
|
|
120
|
+
refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
121
|
+
forceFetch();
|
|
122
|
+
}, refetchIntervalValue));
|
|
123
|
+
}
|
|
116
124
|
onSuccess(response, stateBeforeCallQuery);
|
|
117
125
|
resolve(get());
|
|
118
126
|
})
|
|
@@ -226,26 +234,37 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
226
234
|
}, (() => {
|
|
227
235
|
const fetchWindowFocusHandler = () => {
|
|
228
236
|
useQuery.getAllWithSubscriber().forEach((state) => {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
const result = getValueOrComputedValue(fetchOnWindowFocus, state.key);
|
|
238
|
+
if (result === 'always')
|
|
239
|
+
state.forceFetch();
|
|
240
|
+
else if (result)
|
|
241
|
+
state.fetch();
|
|
233
242
|
});
|
|
234
243
|
};
|
|
235
244
|
return {
|
|
236
245
|
...createStoresOptions,
|
|
237
246
|
defaultDeps,
|
|
238
247
|
onFirstSubscribe: (state) => {
|
|
248
|
+
if (state.isSuccess) {
|
|
249
|
+
const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval);
|
|
250
|
+
if (refetchIntervalValue) {
|
|
251
|
+
refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
252
|
+
state.forceFetch();
|
|
253
|
+
}, refetchIntervalValue));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
239
256
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
240
257
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
241
258
|
}
|
|
259
|
+
clearTimeout(resetTimeoutId.get(state.keyHash));
|
|
242
260
|
onFirstSubscribe(state);
|
|
243
261
|
},
|
|
244
262
|
onSubscribe: (state) => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
263
|
+
const result = getValueOrComputedValue(fetchOnMount, state.key);
|
|
264
|
+
if (result === 'always')
|
|
265
|
+
state.forceFetch();
|
|
266
|
+
else if (result)
|
|
267
|
+
state.fetch();
|
|
249
268
|
onSubscribe(state);
|
|
250
269
|
},
|
|
251
270
|
onLastUnsubscribe: (state) => {
|
|
@@ -255,6 +274,12 @@ export const createQuery = (queryFn, options = {}) => {
|
|
|
255
274
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
256
275
|
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
257
276
|
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
277
|
+
clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
|
|
278
|
+
if (typeof window !== 'undefined' && cacheTime !== Infinity) {
|
|
279
|
+
resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
280
|
+
useQuery.set(state.key, INITIAL_QUERY_STATE);
|
|
281
|
+
}, cacheTime));
|
|
282
|
+
}
|
|
258
283
|
onLastUnsubscribe(state);
|
|
259
284
|
},
|
|
260
285
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
package/esm/utils/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export declare const noop: () => void;
|
|
|
2
2
|
export declare const identityFn: <T>(value: T) => T;
|
|
3
3
|
export declare const hasValue: (value: any) => boolean;
|
|
4
4
|
export declare const hashStoreKey: (obj?: any) => string;
|
|
5
|
+
export declare const getValueOrComputedValue: <T, P extends any[]>(valueOrComputeValueFn: T | ((...params: P) => T), ...params: P) => T;
|
package/esm/utils/index.js
CHANGED
|
@@ -2,3 +2,9 @@ export const noop = () => { };
|
|
|
2
2
|
export const identityFn = (value) => value;
|
|
3
3
|
export const hasValue = (value) => value !== undefined && value !== null;
|
|
4
4
|
export const hashStoreKey = (obj) => JSON.stringify(obj, Object.keys(obj).sort());
|
|
5
|
+
export const getValueOrComputedValue = (valueOrComputeValueFn, ...params) => {
|
|
6
|
+
if (typeof valueOrComputeValueFn === 'function') {
|
|
7
|
+
return valueOrComputeValueFn(...params);
|
|
8
|
+
}
|
|
9
|
+
return valueOrComputeValueFn;
|
|
10
|
+
};
|
|
@@ -72,7 +72,7 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
72
72
|
*
|
|
73
73
|
* `"success"` = has data.
|
|
74
74
|
*
|
|
75
|
-
* `"error"` = has error.
|
|
75
|
+
* `"error"` = has error and no data.
|
|
76
76
|
*
|
|
77
77
|
* It has no relation with network fetching state.
|
|
78
78
|
* If you're looking for network fetching state, use `isWaiting` instead.
|
|
@@ -178,9 +178,29 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
|
|
|
178
178
|
* This function should return a variable that will be used when fetching next page (`pageParam`).
|
|
179
179
|
*/
|
|
180
180
|
getNextPageParam?: (lastPage: TResponse, index: number) => any;
|
|
181
|
+
onBeforeFetch?: (cancel: () => void, state: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
181
182
|
onSuccess?: (response: TResponse, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
182
183
|
onError?: (error: TError, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
183
184
|
onSettled?: (stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
185
|
+
/**
|
|
186
|
+
* Cache time in miliseconds.
|
|
187
|
+
*
|
|
188
|
+
* When a query becomes inactive (no longer have subscribers), it will be reset after this duration,
|
|
189
|
+
* and the cache data will be garbage collected.
|
|
190
|
+
*
|
|
191
|
+
* Set it to `Infinity` to disable garbage collection.
|
|
192
|
+
*
|
|
193
|
+
* Defaults to `5 * 60 * 1000` (5 minutes).
|
|
194
|
+
*/
|
|
195
|
+
cacheTime?: number;
|
|
196
|
+
/**
|
|
197
|
+
* Polling interval in milliseconds.
|
|
198
|
+
*
|
|
199
|
+
* Disabled by default.
|
|
200
|
+
*
|
|
201
|
+
* If the query is on error state, the polling interval will be disabled, and it will use `retry` instead.
|
|
202
|
+
*/
|
|
203
|
+
refetchInterval?: number | false | (() => number | false);
|
|
184
204
|
};
|
|
185
205
|
export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
|
|
186
206
|
/**
|
|
@@ -5,14 +5,6 @@ const preact_1 = require("preact");
|
|
|
5
5
|
const hooks_1 = require("preact/hooks");
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
7
|
const create_stores_1 = require("./create-stores");
|
|
8
|
-
const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
9
|
-
if (value === true || (typeof value === 'function' && value(param) === true)) {
|
|
10
|
-
ifTrue();
|
|
11
|
-
}
|
|
12
|
-
else if (value === 'always' || (typeof value === 'function' && value(param) === 'always')) {
|
|
13
|
-
ifAlways();
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
8
|
const INITIAL_QUERY_STATE = {
|
|
17
9
|
isWaiting: false,
|
|
18
10
|
isWaitingNextPage: false,
|
|
@@ -47,26 +39,36 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
47
39
|
const defaultFetchOnWindowFocus = options.fetchOnMount ?? true;
|
|
48
40
|
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
|
|
49
41
|
fetchOnMount = true, fetchOnWindowFocus = defaultFetchOnWindowFocus, enabled = true, retry = 1, retryDelay = 2000, // 2 seconds
|
|
50
|
-
keepPreviousData, getNextPageParam = () => undefined, onSuccess = utils_1.noop, onError = utils_1.noop, onSettled = utils_1.noop, ...createStoresOptions } = options;
|
|
42
|
+
keepPreviousData, getNextPageParam = () => undefined, onBeforeFetch = utils_1.noop, onSuccess = utils_1.noop, onError = utils_1.noop, onSettled = utils_1.noop, cacheTime = 5 * 60 * 1000, refetchInterval = false, ...createStoresOptions } = options;
|
|
51
43
|
const retryTimeoutId = new Map();
|
|
52
44
|
const retryNextPageTimeoutId = new Map();
|
|
45
|
+
const resetTimeoutId = new Map();
|
|
46
|
+
const refetchIntervalTimeoutId = new Map();
|
|
53
47
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
54
48
|
const useQuery = (0, create_stores_1.createStores)(({ get, set, key: _key, keyHash }) => {
|
|
55
49
|
const key = _key;
|
|
56
50
|
const getRetryProps = (error, retryCount) => {
|
|
57
51
|
const prevState = get();
|
|
58
|
-
const maxRetryCount = (
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
return { shouldRetry, delay };
|
|
52
|
+
const maxRetryCount = (0, utils_1.getValueOrComputedValue)(retry, error, prevState) || 0;
|
|
53
|
+
const delay = (0, utils_1.getValueOrComputedValue)(retryDelay, error, prevState) || 0;
|
|
54
|
+
return { shouldRetry: retryCount < maxRetryCount, delay };
|
|
62
55
|
};
|
|
63
56
|
const forceFetch = () => new Promise((resolve) => {
|
|
64
57
|
const responseAllPages = [];
|
|
65
58
|
const newPageParams = [undefined];
|
|
66
59
|
let pageParam = undefined;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
clearTimeout(refetchIntervalTimeoutId.get(keyHash));
|
|
61
|
+
const state = get();
|
|
62
|
+
const { isWaiting, isLoading, pageParams } = state;
|
|
63
|
+
if (isWaiting || !(0, utils_1.getValueOrComputedValue)(enabled, key))
|
|
64
|
+
return resolve(state);
|
|
65
|
+
let shouldcancel = false;
|
|
66
|
+
const cancel = () => {
|
|
67
|
+
shouldcancel = true;
|
|
68
|
+
};
|
|
69
|
+
onBeforeFetch(cancel, state);
|
|
70
|
+
if (shouldcancel)
|
|
71
|
+
return resolve(state);
|
|
70
72
|
if (isLoading)
|
|
71
73
|
set({ isWaiting: true });
|
|
72
74
|
else
|
|
@@ -117,6 +119,12 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
117
119
|
pageParams: newPageParams,
|
|
118
120
|
hasNextPage: (0, utils_1.hasValue)(newPageParam),
|
|
119
121
|
});
|
|
122
|
+
const refetchIntervalValue = typeof window !== 'undefined' && (0, utils_1.getValueOrComputedValue)(refetchInterval);
|
|
123
|
+
if (refetchIntervalValue) {
|
|
124
|
+
refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
125
|
+
forceFetch();
|
|
126
|
+
}, refetchIntervalValue));
|
|
127
|
+
}
|
|
120
128
|
onSuccess(response, stateBeforeCallQuery);
|
|
121
129
|
resolve(get());
|
|
122
130
|
})
|
|
@@ -230,26 +238,37 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
230
238
|
}, (() => {
|
|
231
239
|
const fetchWindowFocusHandler = () => {
|
|
232
240
|
useQuery.getAllWithSubscriber().forEach((state) => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
241
|
+
const result = (0, utils_1.getValueOrComputedValue)(fetchOnWindowFocus, state.key);
|
|
242
|
+
if (result === 'always')
|
|
243
|
+
state.forceFetch();
|
|
244
|
+
else if (result)
|
|
245
|
+
state.fetch();
|
|
237
246
|
});
|
|
238
247
|
};
|
|
239
248
|
return {
|
|
240
249
|
...createStoresOptions,
|
|
241
250
|
defaultDeps,
|
|
242
251
|
onFirstSubscribe: (state) => {
|
|
252
|
+
if (state.isSuccess) {
|
|
253
|
+
const refetchIntervalValue = typeof window !== 'undefined' && (0, utils_1.getValueOrComputedValue)(refetchInterval);
|
|
254
|
+
if (refetchIntervalValue) {
|
|
255
|
+
refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
256
|
+
state.forceFetch();
|
|
257
|
+
}, refetchIntervalValue));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
243
260
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
244
261
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
245
262
|
}
|
|
263
|
+
clearTimeout(resetTimeoutId.get(state.keyHash));
|
|
246
264
|
onFirstSubscribe(state);
|
|
247
265
|
},
|
|
248
266
|
onSubscribe: (state) => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
267
|
+
const result = (0, utils_1.getValueOrComputedValue)(fetchOnMount, state.key);
|
|
268
|
+
if (result === 'always')
|
|
269
|
+
state.forceFetch();
|
|
270
|
+
else if (result)
|
|
271
|
+
state.fetch();
|
|
253
272
|
onSubscribe(state);
|
|
254
273
|
},
|
|
255
274
|
onLastUnsubscribe: (state) => {
|
|
@@ -259,6 +278,12 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
259
278
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
260
279
|
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
261
280
|
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
281
|
+
clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
|
|
282
|
+
if (typeof window !== 'undefined' && cacheTime !== Infinity) {
|
|
283
|
+
resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
284
|
+
useQuery.set(state.key, INITIAL_QUERY_STATE);
|
|
285
|
+
}, cacheTime));
|
|
286
|
+
}
|
|
262
287
|
onLastUnsubscribe(state);
|
|
263
288
|
},
|
|
264
289
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
|
@@ -71,7 +71,7 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
|
|
|
71
71
|
*
|
|
72
72
|
* `"success"` = has data.
|
|
73
73
|
*
|
|
74
|
-
* `"error"` = has error.
|
|
74
|
+
* `"error"` = has error and no data.
|
|
75
75
|
*
|
|
76
76
|
* It has no relation with network fetching state.
|
|
77
77
|
* If you're looking for network fetching state, use `isWaiting` instead.
|
|
@@ -177,9 +177,29 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
|
|
|
177
177
|
* This function should return a variable that will be used when fetching next page (`pageParam`).
|
|
178
178
|
*/
|
|
179
179
|
getNextPageParam?: (lastPage: TResponse, index: number) => any;
|
|
180
|
+
onBeforeFetch?: (cancel: () => void, state: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
180
181
|
onSuccess?: (response: TResponse, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
181
182
|
onError?: (error: TError, stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
182
183
|
onSettled?: (stateBeforeCallQuery: QueryState<TKey, TResponse, TData, TError>) => void;
|
|
184
|
+
/**
|
|
185
|
+
* Cache time in miliseconds.
|
|
186
|
+
*
|
|
187
|
+
* When a query becomes inactive (no longer have subscribers), it will be reset after this duration,
|
|
188
|
+
* and the cache data will be garbage collected.
|
|
189
|
+
*
|
|
190
|
+
* Set it to `Infinity` to disable garbage collection.
|
|
191
|
+
*
|
|
192
|
+
* Defaults to `5 * 60 * 1000` (5 minutes).
|
|
193
|
+
*/
|
|
194
|
+
cacheTime?: number;
|
|
195
|
+
/**
|
|
196
|
+
* Polling interval in milliseconds.
|
|
197
|
+
*
|
|
198
|
+
* Disabled by default.
|
|
199
|
+
*
|
|
200
|
+
* If the query is on error state, the polling interval will be disabled, and it will use `retry` instead.
|
|
201
|
+
*/
|
|
202
|
+
refetchInterval?: number | false | (() => number | false);
|
|
183
203
|
};
|
|
184
204
|
export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
|
|
185
205
|
/**
|
|
@@ -4,14 +4,6 @@ exports.createQuery = void 0;
|
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
const create_stores_1 = require("./create-stores");
|
|
7
|
-
const getDecision = (value, param, { ifTrue, ifAlways }) => {
|
|
8
|
-
if (value === true || (typeof value === 'function' && value(param) === true)) {
|
|
9
|
-
ifTrue();
|
|
10
|
-
}
|
|
11
|
-
else if (value === 'always' || (typeof value === 'function' && value(param) === 'always')) {
|
|
12
|
-
ifAlways();
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
7
|
const INITIAL_QUERY_STATE = {
|
|
16
8
|
isWaiting: false,
|
|
17
9
|
isWaitingNextPage: false,
|
|
@@ -46,26 +38,36 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
46
38
|
const defaultFetchOnWindowFocus = options.fetchOnMount ?? true;
|
|
47
39
|
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
|
|
48
40
|
fetchOnMount = true, fetchOnWindowFocus = defaultFetchOnWindowFocus, enabled = true, retry = 1, retryDelay = 2000, // 2 seconds
|
|
49
|
-
keepPreviousData, getNextPageParam = () => undefined, onSuccess = utils_1.noop, onError = utils_1.noop, onSettled = utils_1.noop, ...createStoresOptions } = options;
|
|
41
|
+
keepPreviousData, getNextPageParam = () => undefined, onBeforeFetch = utils_1.noop, onSuccess = utils_1.noop, onError = utils_1.noop, onSettled = utils_1.noop, cacheTime = 5 * 60 * 1000, refetchInterval = false, ...createStoresOptions } = options;
|
|
50
42
|
const retryTimeoutId = new Map();
|
|
51
43
|
const retryNextPageTimeoutId = new Map();
|
|
44
|
+
const resetTimeoutId = new Map();
|
|
45
|
+
const refetchIntervalTimeoutId = new Map();
|
|
52
46
|
const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
|
|
53
47
|
const useQuery = (0, create_stores_1.createStores)(({ get, set, key: _key, keyHash }) => {
|
|
54
48
|
const key = _key;
|
|
55
49
|
const getRetryProps = (error, retryCount) => {
|
|
56
50
|
const prevState = get();
|
|
57
|
-
const maxRetryCount = (
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
return { shouldRetry, delay };
|
|
51
|
+
const maxRetryCount = (0, utils_1.getValueOrComputedValue)(retry, error, prevState) || 0;
|
|
52
|
+
const delay = (0, utils_1.getValueOrComputedValue)(retryDelay, error, prevState) || 0;
|
|
53
|
+
return { shouldRetry: retryCount < maxRetryCount, delay };
|
|
61
54
|
};
|
|
62
55
|
const forceFetch = () => new Promise((resolve) => {
|
|
63
56
|
const responseAllPages = [];
|
|
64
57
|
const newPageParams = [undefined];
|
|
65
58
|
let pageParam = undefined;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
clearTimeout(refetchIntervalTimeoutId.get(keyHash));
|
|
60
|
+
const state = get();
|
|
61
|
+
const { isWaiting, isLoading, pageParams } = state;
|
|
62
|
+
if (isWaiting || !(0, utils_1.getValueOrComputedValue)(enabled, key))
|
|
63
|
+
return resolve(state);
|
|
64
|
+
let shouldcancel = false;
|
|
65
|
+
const cancel = () => {
|
|
66
|
+
shouldcancel = true;
|
|
67
|
+
};
|
|
68
|
+
onBeforeFetch(cancel, state);
|
|
69
|
+
if (shouldcancel)
|
|
70
|
+
return resolve(state);
|
|
69
71
|
if (isLoading)
|
|
70
72
|
set({ isWaiting: true });
|
|
71
73
|
else
|
|
@@ -116,6 +118,12 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
116
118
|
pageParams: newPageParams,
|
|
117
119
|
hasNextPage: (0, utils_1.hasValue)(newPageParam),
|
|
118
120
|
});
|
|
121
|
+
const refetchIntervalValue = typeof window !== 'undefined' && (0, utils_1.getValueOrComputedValue)(refetchInterval);
|
|
122
|
+
if (refetchIntervalValue) {
|
|
123
|
+
refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
|
|
124
|
+
forceFetch();
|
|
125
|
+
}, refetchIntervalValue));
|
|
126
|
+
}
|
|
119
127
|
onSuccess(response, stateBeforeCallQuery);
|
|
120
128
|
resolve(get());
|
|
121
129
|
})
|
|
@@ -229,26 +237,37 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
229
237
|
}, (() => {
|
|
230
238
|
const fetchWindowFocusHandler = () => {
|
|
231
239
|
useQuery.getAllWithSubscriber().forEach((state) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
240
|
+
const result = (0, utils_1.getValueOrComputedValue)(fetchOnWindowFocus, state.key);
|
|
241
|
+
if (result === 'always')
|
|
242
|
+
state.forceFetch();
|
|
243
|
+
else if (result)
|
|
244
|
+
state.fetch();
|
|
236
245
|
});
|
|
237
246
|
};
|
|
238
247
|
return {
|
|
239
248
|
...createStoresOptions,
|
|
240
249
|
defaultDeps,
|
|
241
250
|
onFirstSubscribe: (state) => {
|
|
251
|
+
if (state.isSuccess) {
|
|
252
|
+
const refetchIntervalValue = typeof window !== 'undefined' && (0, utils_1.getValueOrComputedValue)(refetchInterval);
|
|
253
|
+
if (refetchIntervalValue) {
|
|
254
|
+
refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
255
|
+
state.forceFetch();
|
|
256
|
+
}, refetchIntervalValue));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
242
259
|
if (typeof window !== 'undefined' && fetchOnWindowFocus) {
|
|
243
260
|
window.addEventListener('focus', fetchWindowFocusHandler);
|
|
244
261
|
}
|
|
262
|
+
clearTimeout(resetTimeoutId.get(state.keyHash));
|
|
245
263
|
onFirstSubscribe(state);
|
|
246
264
|
},
|
|
247
265
|
onSubscribe: (state) => {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
266
|
+
const result = (0, utils_1.getValueOrComputedValue)(fetchOnMount, state.key);
|
|
267
|
+
if (result === 'always')
|
|
268
|
+
state.forceFetch();
|
|
269
|
+
else if (result)
|
|
270
|
+
state.fetch();
|
|
252
271
|
onSubscribe(state);
|
|
253
272
|
},
|
|
254
273
|
onLastUnsubscribe: (state) => {
|
|
@@ -258,6 +277,12 @@ const createQuery = (queryFn, options = {}) => {
|
|
|
258
277
|
useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
|
|
259
278
|
clearTimeout(retryTimeoutId.get(state.keyHash));
|
|
260
279
|
clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
|
|
280
|
+
clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
|
|
281
|
+
if (typeof window !== 'undefined' && cacheTime !== Infinity) {
|
|
282
|
+
resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
|
|
283
|
+
useQuery.set(state.key, INITIAL_QUERY_STATE);
|
|
284
|
+
}, cacheTime));
|
|
285
|
+
}
|
|
261
286
|
onLastUnsubscribe(state);
|
|
262
287
|
},
|
|
263
288
|
onBeforeChangeKey: (nextKey, prevKey) => {
|
package/lib/utils/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export declare const noop: () => void;
|
|
|
2
2
|
export declare const identityFn: <T>(value: T) => T;
|
|
3
3
|
export declare const hasValue: (value: any) => boolean;
|
|
4
4
|
export declare const hashStoreKey: (obj?: any) => string;
|
|
5
|
+
export declare const getValueOrComputedValue: <T, P extends any[]>(valueOrComputeValueFn: T | ((...params: P) => T), ...params: P) => T;
|
package/lib/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hashStoreKey = exports.hasValue = exports.identityFn = exports.noop = void 0;
|
|
3
|
+
exports.getValueOrComputedValue = exports.hashStoreKey = exports.hasValue = exports.identityFn = exports.noop = void 0;
|
|
4
4
|
const noop = () => { };
|
|
5
5
|
exports.noop = noop;
|
|
6
6
|
const identityFn = (value) => value;
|
|
@@ -9,3 +9,10 @@ const hasValue = (value) => value !== undefined && value !== null;
|
|
|
9
9
|
exports.hasValue = hasValue;
|
|
10
10
|
const hashStoreKey = (obj) => JSON.stringify(obj, Object.keys(obj).sort());
|
|
11
11
|
exports.hashStoreKey = hashStoreKey;
|
|
12
|
+
const getValueOrComputedValue = (valueOrComputeValueFn, ...params) => {
|
|
13
|
+
if (typeof valueOrComputeValueFn === 'function') {
|
|
14
|
+
return valueOrComputeValueFn(...params);
|
|
15
|
+
}
|
|
16
|
+
return valueOrComputeValueFn;
|
|
17
|
+
};
|
|
18
|
+
exports.getValueOrComputedValue = getValueOrComputedValue;
|