floppy-disk 2.4.0-beta.1 → 2.4.0-beta.3

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.
@@ -178,6 +178,7 @@ 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;
@@ -192,6 +193,14 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
192
193
  * Defaults to `5 * 60 * 1000` (5 minutes).
193
194
  */
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 | ((state: QueryState<TKey, TResponse, TData, TError>) => number | false);
195
204
  };
196
205
  export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
197
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,27 +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, cacheTime = 5 * 60 * 1000, ...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();
50
42
  const resetTimeoutId = new Map();
43
+ const refetchIntervalTimeoutId = new Map();
51
44
  const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
52
45
  const useQuery = createStores(({ get, set, key: _key, keyHash }) => {
53
46
  const key = _key;
54
47
  const getRetryProps = (error, retryCount) => {
55
48
  const prevState = get();
56
- const maxRetryCount = (typeof retry === 'function' ? retry(error, prevState) : retry) || 0;
57
- const shouldRetry = retryCount < maxRetryCount;
58
- const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
59
- 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 };
60
52
  };
61
53
  const forceFetch = () => new Promise((resolve) => {
62
54
  const responseAllPages = [];
63
55
  const newPageParams = [undefined];
64
56
  let pageParam = undefined;
65
- const { isWaiting, isLoading, pageParams } = get();
66
- if (isWaiting || enabled === false || (typeof enabled === 'function' && !enabled(key)))
67
- return;
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);
68
69
  if (isLoading)
69
70
  set({ isWaiting: true });
70
71
  else
@@ -93,7 +94,7 @@ export const createQuery = (queryFn, options = {}) => {
93
94
  callQuery();
94
95
  return;
95
96
  }
96
- set({
97
+ const nextState = {
97
98
  isWaiting: false,
98
99
  status: 'success',
99
100
  isLoading: false,
@@ -114,7 +115,15 @@ export const createQuery = (queryFn, options = {}) => {
114
115
  pageParam: newPageParam,
115
116
  pageParams: newPageParams,
116
117
  hasNextPage: hasValue(newPageParam),
117
- });
118
+ };
119
+ const refetchIntervalValue = typeof window !== 'undefined' &&
120
+ getValueOrComputedValue(refetchInterval, { ...get(), ...nextState });
121
+ if (refetchIntervalValue) {
122
+ refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
123
+ forceFetch();
124
+ }, refetchIntervalValue));
125
+ }
126
+ set(nextState);
118
127
  onSuccess(response, stateBeforeCallQuery);
119
128
  resolve(get());
120
129
  })
@@ -228,16 +237,25 @@ export const createQuery = (queryFn, options = {}) => {
228
237
  }, (() => {
229
238
  const fetchWindowFocusHandler = () => {
230
239
  useQuery.getAllWithSubscriber().forEach((state) => {
231
- getDecision(fetchOnWindowFocus, state.key, {
232
- ifTrue: state.fetch,
233
- ifAlways: state.forceFetch,
234
- });
240
+ const result = getValueOrComputedValue(fetchOnWindowFocus, state.key);
241
+ if (result === 'always')
242
+ state.forceFetch();
243
+ else if (result)
244
+ state.fetch();
235
245
  });
236
246
  };
237
247
  return {
238
248
  ...createStoresOptions,
239
249
  defaultDeps,
240
250
  onFirstSubscribe: (state) => {
251
+ if (state.isSuccess) {
252
+ const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval, state);
253
+ if (refetchIntervalValue) {
254
+ refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
255
+ state.forceFetch();
256
+ }, refetchIntervalValue));
257
+ }
258
+ }
241
259
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
242
260
  window.addEventListener('focus', fetchWindowFocusHandler);
243
261
  }
@@ -245,10 +263,11 @@ export const createQuery = (queryFn, options = {}) => {
245
263
  onFirstSubscribe(state);
246
264
  },
247
265
  onSubscribe: (state) => {
248
- getDecision(fetchOnMount, state.key, {
249
- ifTrue: state.fetch,
250
- ifAlways: state.forceFetch,
251
- });
266
+ const result = 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,7 @@ export 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));
261
281
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
262
282
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
263
283
  useQuery.set(state.key, INITIAL_QUERY_STATE);
@@ -177,6 +177,7 @@ 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;
@@ -191,6 +192,14 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
191
192
  * Defaults to `5 * 60 * 1000` (5 minutes).
192
193
  */
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 | ((state: QueryState<TKey, TResponse, TData, TError>) => number | false);
194
203
  };
195
204
  export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
196
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,27 +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, cacheTime = 5 * 60 * 1000, ...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();
49
41
  const resetTimeoutId = new Map();
42
+ const refetchIntervalTimeoutId = new Map();
50
43
  const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
51
44
  const useQuery = createStores(({ get, set, key: _key, keyHash }) => {
52
45
  const key = _key;
53
46
  const getRetryProps = (error, retryCount) => {
54
47
  const prevState = get();
55
- const maxRetryCount = (typeof retry === 'function' ? retry(error, prevState) : retry) || 0;
56
- const shouldRetry = retryCount < maxRetryCount;
57
- const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
58
- 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 };
59
51
  };
60
52
  const forceFetch = () => new Promise((resolve) => {
61
53
  const responseAllPages = [];
62
54
  const newPageParams = [undefined];
63
55
  let pageParam = undefined;
64
- const { isWaiting, isLoading, pageParams } = get();
65
- if (isWaiting || enabled === false || (typeof enabled === 'function' && !enabled(key)))
66
- return;
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);
67
68
  if (isLoading)
68
69
  set({ isWaiting: true });
69
70
  else
@@ -92,7 +93,7 @@ export const createQuery = (queryFn, options = {}) => {
92
93
  callQuery();
93
94
  return;
94
95
  }
95
- set({
96
+ const nextState = {
96
97
  isWaiting: false,
97
98
  status: 'success',
98
99
  isLoading: false,
@@ -113,7 +114,15 @@ export const createQuery = (queryFn, options = {}) => {
113
114
  pageParam: newPageParam,
114
115
  pageParams: newPageParams,
115
116
  hasNextPage: hasValue(newPageParam),
116
- });
117
+ };
118
+ const refetchIntervalValue = typeof window !== 'undefined' &&
119
+ getValueOrComputedValue(refetchInterval, { ...get(), ...nextState });
120
+ if (refetchIntervalValue) {
121
+ refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
122
+ forceFetch();
123
+ }, refetchIntervalValue));
124
+ }
125
+ set(nextState);
117
126
  onSuccess(response, stateBeforeCallQuery);
118
127
  resolve(get());
119
128
  })
@@ -227,16 +236,25 @@ export const createQuery = (queryFn, options = {}) => {
227
236
  }, (() => {
228
237
  const fetchWindowFocusHandler = () => {
229
238
  useQuery.getAllWithSubscriber().forEach((state) => {
230
- getDecision(fetchOnWindowFocus, state.key, {
231
- ifTrue: state.fetch,
232
- ifAlways: state.forceFetch,
233
- });
239
+ const result = getValueOrComputedValue(fetchOnWindowFocus, state.key);
240
+ if (result === 'always')
241
+ state.forceFetch();
242
+ else if (result)
243
+ state.fetch();
234
244
  });
235
245
  };
236
246
  return {
237
247
  ...createStoresOptions,
238
248
  defaultDeps,
239
249
  onFirstSubscribe: (state) => {
250
+ if (state.isSuccess) {
251
+ const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval, state);
252
+ if (refetchIntervalValue) {
253
+ refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
254
+ state.forceFetch();
255
+ }, refetchIntervalValue));
256
+ }
257
+ }
240
258
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
241
259
  window.addEventListener('focus', fetchWindowFocusHandler);
242
260
  }
@@ -244,10 +262,11 @@ export const createQuery = (queryFn, options = {}) => {
244
262
  onFirstSubscribe(state);
245
263
  },
246
264
  onSubscribe: (state) => {
247
- getDecision(fetchOnMount, state.key, {
248
- ifTrue: state.fetch,
249
- ifAlways: state.forceFetch,
250
- });
265
+ const result = getValueOrComputedValue(fetchOnMount, state.key);
266
+ if (result === 'always')
267
+ state.forceFetch();
268
+ else if (result)
269
+ state.fetch();
251
270
  onSubscribe(state);
252
271
  },
253
272
  onLastUnsubscribe: (state) => {
@@ -257,6 +276,7 @@ export const createQuery = (queryFn, options = {}) => {
257
276
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
258
277
  clearTimeout(retryTimeoutId.get(state.keyHash));
259
278
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
279
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
260
280
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
261
281
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
262
282
  useQuery.set(state.key, INITIAL_QUERY_STATE);
@@ -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;
@@ -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
+ };
@@ -178,6 +178,7 @@ 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;
@@ -192,6 +193,14 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
192
193
  * Defaults to `5 * 60 * 1000` (5 minutes).
193
194
  */
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 | ((state: QueryState<TKey, TResponse, TData, TError>) => number | false);
195
204
  };
196
205
  export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
197
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,27 +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, cacheTime = 5 * 60 * 1000, ...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();
53
45
  const resetTimeoutId = new Map();
46
+ const refetchIntervalTimeoutId = new Map();
54
47
  const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
55
48
  const useQuery = (0, create_stores_1.createStores)(({ get, set, key: _key, keyHash }) => {
56
49
  const key = _key;
57
50
  const getRetryProps = (error, retryCount) => {
58
51
  const prevState = get();
59
- const maxRetryCount = (typeof retry === 'function' ? retry(error, prevState) : retry) || 0;
60
- const shouldRetry = retryCount < maxRetryCount;
61
- const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
62
- 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 };
63
55
  };
64
56
  const forceFetch = () => new Promise((resolve) => {
65
57
  const responseAllPages = [];
66
58
  const newPageParams = [undefined];
67
59
  let pageParam = undefined;
68
- const { isWaiting, isLoading, pageParams } = get();
69
- if (isWaiting || enabled === false || (typeof enabled === 'function' && !enabled(key)))
70
- return;
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);
71
72
  if (isLoading)
72
73
  set({ isWaiting: true });
73
74
  else
@@ -96,7 +97,7 @@ const createQuery = (queryFn, options = {}) => {
96
97
  callQuery();
97
98
  return;
98
99
  }
99
- set({
100
+ const nextState = {
100
101
  isWaiting: false,
101
102
  status: 'success',
102
103
  isLoading: false,
@@ -117,7 +118,15 @@ const createQuery = (queryFn, options = {}) => {
117
118
  pageParam: newPageParam,
118
119
  pageParams: newPageParams,
119
120
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
120
- });
121
+ };
122
+ const refetchIntervalValue = typeof window !== 'undefined' &&
123
+ (0, utils_1.getValueOrComputedValue)(refetchInterval, { ...get(), ...nextState });
124
+ if (refetchIntervalValue) {
125
+ refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
126
+ forceFetch();
127
+ }, refetchIntervalValue));
128
+ }
129
+ set(nextState);
121
130
  onSuccess(response, stateBeforeCallQuery);
122
131
  resolve(get());
123
132
  })
@@ -231,16 +240,25 @@ const createQuery = (queryFn, options = {}) => {
231
240
  }, (() => {
232
241
  const fetchWindowFocusHandler = () => {
233
242
  useQuery.getAllWithSubscriber().forEach((state) => {
234
- getDecision(fetchOnWindowFocus, state.key, {
235
- ifTrue: state.fetch,
236
- ifAlways: state.forceFetch,
237
- });
243
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnWindowFocus, state.key);
244
+ if (result === 'always')
245
+ state.forceFetch();
246
+ else if (result)
247
+ state.fetch();
238
248
  });
239
249
  };
240
250
  return {
241
251
  ...createStoresOptions,
242
252
  defaultDeps,
243
253
  onFirstSubscribe: (state) => {
254
+ if (state.isSuccess) {
255
+ const refetchIntervalValue = typeof window !== 'undefined' && (0, utils_1.getValueOrComputedValue)(refetchInterval, state);
256
+ if (refetchIntervalValue) {
257
+ refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
258
+ state.forceFetch();
259
+ }, refetchIntervalValue));
260
+ }
261
+ }
244
262
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
245
263
  window.addEventListener('focus', fetchWindowFocusHandler);
246
264
  }
@@ -248,10 +266,11 @@ const createQuery = (queryFn, options = {}) => {
248
266
  onFirstSubscribe(state);
249
267
  },
250
268
  onSubscribe: (state) => {
251
- getDecision(fetchOnMount, state.key, {
252
- ifTrue: state.fetch,
253
- ifAlways: state.forceFetch,
254
- });
269
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnMount, state.key);
270
+ if (result === 'always')
271
+ state.forceFetch();
272
+ else if (result)
273
+ state.fetch();
255
274
  onSubscribe(state);
256
275
  },
257
276
  onLastUnsubscribe: (state) => {
@@ -261,6 +280,7 @@ const createQuery = (queryFn, options = {}) => {
261
280
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
262
281
  clearTimeout(retryTimeoutId.get(state.keyHash));
263
282
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
283
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
264
284
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
265
285
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
266
286
  useQuery.set(state.key, INITIAL_QUERY_STATE);
@@ -177,6 +177,7 @@ 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;
@@ -191,6 +192,14 @@ export type CreateQueryOptions<TKey extends StoreKey = StoreKey, TResponse = any
191
192
  * Defaults to `5 * 60 * 1000` (5 minutes).
192
193
  */
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 | ((state: QueryState<TKey, TResponse, TData, TError>) => number | false);
194
203
  };
195
204
  export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = UseStores<TKey, QueryState<TKey, TResponse, TData, TError>> & {
196
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,27 +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, cacheTime = 5 * 60 * 1000, ...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();
52
44
  const resetTimeoutId = new Map();
45
+ const refetchIntervalTimeoutId = new Map();
53
46
  const preventReplaceResponse = new Map(); // Prevent optimistic data to be replaced
54
47
  const useQuery = (0, create_stores_1.createStores)(({ get, set, key: _key, keyHash }) => {
55
48
  const key = _key;
56
49
  const getRetryProps = (error, retryCount) => {
57
50
  const prevState = get();
58
- const maxRetryCount = (typeof retry === 'function' ? retry(error, prevState) : retry) || 0;
59
- const shouldRetry = retryCount < maxRetryCount;
60
- const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
61
- 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 };
62
54
  };
63
55
  const forceFetch = () => new Promise((resolve) => {
64
56
  const responseAllPages = [];
65
57
  const newPageParams = [undefined];
66
58
  let pageParam = undefined;
67
- const { isWaiting, isLoading, pageParams } = get();
68
- if (isWaiting || enabled === false || (typeof enabled === 'function' && !enabled(key)))
69
- return;
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);
70
71
  if (isLoading)
71
72
  set({ isWaiting: true });
72
73
  else
@@ -95,7 +96,7 @@ const createQuery = (queryFn, options = {}) => {
95
96
  callQuery();
96
97
  return;
97
98
  }
98
- set({
99
+ const nextState = {
99
100
  isWaiting: false,
100
101
  status: 'success',
101
102
  isLoading: false,
@@ -116,7 +117,15 @@ const createQuery = (queryFn, options = {}) => {
116
117
  pageParam: newPageParam,
117
118
  pageParams: newPageParams,
118
119
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
119
- });
120
+ };
121
+ const refetchIntervalValue = typeof window !== 'undefined' &&
122
+ (0, utils_1.getValueOrComputedValue)(refetchInterval, { ...get(), ...nextState });
123
+ if (refetchIntervalValue) {
124
+ refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
125
+ forceFetch();
126
+ }, refetchIntervalValue));
127
+ }
128
+ set(nextState);
120
129
  onSuccess(response, stateBeforeCallQuery);
121
130
  resolve(get());
122
131
  })
@@ -230,16 +239,25 @@ const createQuery = (queryFn, options = {}) => {
230
239
  }, (() => {
231
240
  const fetchWindowFocusHandler = () => {
232
241
  useQuery.getAllWithSubscriber().forEach((state) => {
233
- getDecision(fetchOnWindowFocus, state.key, {
234
- ifTrue: state.fetch,
235
- ifAlways: state.forceFetch,
236
- });
242
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnWindowFocus, state.key);
243
+ if (result === 'always')
244
+ state.forceFetch();
245
+ else if (result)
246
+ state.fetch();
237
247
  });
238
248
  };
239
249
  return {
240
250
  ...createStoresOptions,
241
251
  defaultDeps,
242
252
  onFirstSubscribe: (state) => {
253
+ if (state.isSuccess) {
254
+ const refetchIntervalValue = typeof window !== 'undefined' && (0, utils_1.getValueOrComputedValue)(refetchInterval, state);
255
+ if (refetchIntervalValue) {
256
+ refetchIntervalTimeoutId.set(state.keyHash, window.setTimeout(() => {
257
+ state.forceFetch();
258
+ }, refetchIntervalValue));
259
+ }
260
+ }
243
261
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
244
262
  window.addEventListener('focus', fetchWindowFocusHandler);
245
263
  }
@@ -247,10 +265,11 @@ const createQuery = (queryFn, options = {}) => {
247
265
  onFirstSubscribe(state);
248
266
  },
249
267
  onSubscribe: (state) => {
250
- getDecision(fetchOnMount, state.key, {
251
- ifTrue: state.fetch,
252
- ifAlways: state.forceFetch,
253
- });
268
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnMount, state.key);
269
+ if (result === 'always')
270
+ state.forceFetch();
271
+ else if (result)
272
+ state.fetch();
254
273
  onSubscribe(state);
255
274
  },
256
275
  onLastUnsubscribe: (state) => {
@@ -260,6 +279,7 @@ const createQuery = (queryFn, options = {}) => {
260
279
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
261
280
  clearTimeout(retryTimeoutId.get(state.keyHash));
262
281
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
282
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
263
283
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
264
284
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
265
285
  useQuery.set(state.key, INITIAL_QUERY_STATE);
@@ -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;
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floppy-disk",
3
- "version": "2.4.0-beta.1",
3
+ "version": "2.4.0-beta.3",
4
4
  "description": "FloppyDisk - lightweight, simple, and powerful state management library",
5
5
  "keywords": [
6
6
  "state",