floppy-disk 2.4.0-beta.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.
@@ -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 | (() => 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
@@ -115,6 +116,12 @@ export const createQuery = (queryFn, options = {}) => {
115
116
  pageParams: newPageParams,
116
117
  hasNextPage: hasValue(newPageParam),
117
118
  });
119
+ const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval);
120
+ if (refetchIntervalValue) {
121
+ refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
122
+ forceFetch();
123
+ }, refetchIntervalValue));
124
+ }
118
125
  onSuccess(response, stateBeforeCallQuery);
119
126
  resolve(get());
120
127
  })
@@ -228,16 +235,25 @@ export const createQuery = (queryFn, options = {}) => {
228
235
  }, (() => {
229
236
  const fetchWindowFocusHandler = () => {
230
237
  useQuery.getAllWithSubscriber().forEach((state) => {
231
- getDecision(fetchOnWindowFocus, state.key, {
232
- ifTrue: state.fetch,
233
- ifAlways: state.forceFetch,
234
- });
238
+ const result = getValueOrComputedValue(fetchOnWindowFocus, state.key);
239
+ if (result === 'always')
240
+ state.forceFetch();
241
+ else if (result)
242
+ state.fetch();
235
243
  });
236
244
  };
237
245
  return {
238
246
  ...createStoresOptions,
239
247
  defaultDeps,
240
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
+ }
241
257
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
242
258
  window.addEventListener('focus', fetchWindowFocusHandler);
243
259
  }
@@ -245,10 +261,11 @@ export const createQuery = (queryFn, options = {}) => {
245
261
  onFirstSubscribe(state);
246
262
  },
247
263
  onSubscribe: (state) => {
248
- getDecision(fetchOnMount, state.key, {
249
- ifTrue: state.fetch,
250
- ifAlways: state.forceFetch,
251
- });
264
+ const result = getValueOrComputedValue(fetchOnMount, state.key);
265
+ if (result === 'always')
266
+ state.forceFetch();
267
+ else if (result)
268
+ state.fetch();
252
269
  onSubscribe(state);
253
270
  },
254
271
  onLastUnsubscribe: (state) => {
@@ -258,6 +275,7 @@ export const createQuery = (queryFn, options = {}) => {
258
275
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
259
276
  clearTimeout(retryTimeoutId.get(state.keyHash));
260
277
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
278
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
261
279
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
262
280
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
263
281
  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 | (() => 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
@@ -114,6 +115,12 @@ export const createQuery = (queryFn, options = {}) => {
114
115
  pageParams: newPageParams,
115
116
  hasNextPage: hasValue(newPageParam),
116
117
  });
118
+ const refetchIntervalValue = typeof window !== 'undefined' && getValueOrComputedValue(refetchInterval);
119
+ if (refetchIntervalValue) {
120
+ refetchIntervalTimeoutId.set(keyHash, window.setTimeout(() => {
121
+ forceFetch();
122
+ }, refetchIntervalValue));
123
+ }
117
124
  onSuccess(response, stateBeforeCallQuery);
118
125
  resolve(get());
119
126
  })
@@ -227,16 +234,25 @@ export const createQuery = (queryFn, options = {}) => {
227
234
  }, (() => {
228
235
  const fetchWindowFocusHandler = () => {
229
236
  useQuery.getAllWithSubscriber().forEach((state) => {
230
- getDecision(fetchOnWindowFocus, state.key, {
231
- ifTrue: state.fetch,
232
- ifAlways: state.forceFetch,
233
- });
237
+ const result = getValueOrComputedValue(fetchOnWindowFocus, state.key);
238
+ if (result === 'always')
239
+ state.forceFetch();
240
+ else if (result)
241
+ state.fetch();
234
242
  });
235
243
  };
236
244
  return {
237
245
  ...createStoresOptions,
238
246
  defaultDeps,
239
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
+ }
240
256
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
241
257
  window.addEventListener('focus', fetchWindowFocusHandler);
242
258
  }
@@ -244,10 +260,11 @@ export const createQuery = (queryFn, options = {}) => {
244
260
  onFirstSubscribe(state);
245
261
  },
246
262
  onSubscribe: (state) => {
247
- getDecision(fetchOnMount, state.key, {
248
- ifTrue: state.fetch,
249
- ifAlways: state.forceFetch,
250
- });
263
+ const result = getValueOrComputedValue(fetchOnMount, state.key);
264
+ if (result === 'always')
265
+ state.forceFetch();
266
+ else if (result)
267
+ state.fetch();
251
268
  onSubscribe(state);
252
269
  },
253
270
  onLastUnsubscribe: (state) => {
@@ -257,6 +274,7 @@ export const createQuery = (queryFn, options = {}) => {
257
274
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
258
275
  clearTimeout(retryTimeoutId.get(state.keyHash));
259
276
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
277
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
260
278
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
261
279
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
262
280
  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 | (() => 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
@@ -118,6 +119,12 @@ const createQuery = (queryFn, options = {}) => {
118
119
  pageParams: newPageParams,
119
120
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
120
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
+ }
121
128
  onSuccess(response, stateBeforeCallQuery);
122
129
  resolve(get());
123
130
  })
@@ -231,16 +238,25 @@ const createQuery = (queryFn, options = {}) => {
231
238
  }, (() => {
232
239
  const fetchWindowFocusHandler = () => {
233
240
  useQuery.getAllWithSubscriber().forEach((state) => {
234
- getDecision(fetchOnWindowFocus, state.key, {
235
- ifTrue: state.fetch,
236
- ifAlways: state.forceFetch,
237
- });
241
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnWindowFocus, state.key);
242
+ if (result === 'always')
243
+ state.forceFetch();
244
+ else if (result)
245
+ state.fetch();
238
246
  });
239
247
  };
240
248
  return {
241
249
  ...createStoresOptions,
242
250
  defaultDeps,
243
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
+ }
244
260
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
245
261
  window.addEventListener('focus', fetchWindowFocusHandler);
246
262
  }
@@ -248,10 +264,11 @@ const createQuery = (queryFn, options = {}) => {
248
264
  onFirstSubscribe(state);
249
265
  },
250
266
  onSubscribe: (state) => {
251
- getDecision(fetchOnMount, state.key, {
252
- ifTrue: state.fetch,
253
- ifAlways: state.forceFetch,
254
- });
267
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnMount, state.key);
268
+ if (result === 'always')
269
+ state.forceFetch();
270
+ else if (result)
271
+ state.fetch();
255
272
  onSubscribe(state);
256
273
  },
257
274
  onLastUnsubscribe: (state) => {
@@ -261,6 +278,7 @@ const createQuery = (queryFn, options = {}) => {
261
278
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
262
279
  clearTimeout(retryTimeoutId.get(state.keyHash));
263
280
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
281
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
264
282
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
265
283
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
266
284
  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 | (() => 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
@@ -117,6 +118,12 @@ const createQuery = (queryFn, options = {}) => {
117
118
  pageParams: newPageParams,
118
119
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
119
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
+ }
120
127
  onSuccess(response, stateBeforeCallQuery);
121
128
  resolve(get());
122
129
  })
@@ -230,16 +237,25 @@ const createQuery = (queryFn, options = {}) => {
230
237
  }, (() => {
231
238
  const fetchWindowFocusHandler = () => {
232
239
  useQuery.getAllWithSubscriber().forEach((state) => {
233
- getDecision(fetchOnWindowFocus, state.key, {
234
- ifTrue: state.fetch,
235
- ifAlways: state.forceFetch,
236
- });
240
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnWindowFocus, state.key);
241
+ if (result === 'always')
242
+ state.forceFetch();
243
+ else if (result)
244
+ state.fetch();
237
245
  });
238
246
  };
239
247
  return {
240
248
  ...createStoresOptions,
241
249
  defaultDeps,
242
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
+ }
243
259
  if (typeof window !== 'undefined' && fetchOnWindowFocus) {
244
260
  window.addEventListener('focus', fetchWindowFocusHandler);
245
261
  }
@@ -247,10 +263,11 @@ const createQuery = (queryFn, options = {}) => {
247
263
  onFirstSubscribe(state);
248
264
  },
249
265
  onSubscribe: (state) => {
250
- getDecision(fetchOnMount, state.key, {
251
- ifTrue: state.fetch,
252
- ifAlways: state.forceFetch,
253
- });
266
+ const result = (0, utils_1.getValueOrComputedValue)(fetchOnMount, state.key);
267
+ if (result === 'always')
268
+ state.forceFetch();
269
+ else if (result)
270
+ state.fetch();
254
271
  onSubscribe(state);
255
272
  },
256
273
  onLastUnsubscribe: (state) => {
@@ -260,6 +277,7 @@ const createQuery = (queryFn, options = {}) => {
260
277
  useQuery.set(state.key, { retryCount: 0, retryNextPageCount: 0 }, true);
261
278
  clearTimeout(retryTimeoutId.get(state.keyHash));
262
279
  clearTimeout(retryNextPageTimeoutId.get(state.keyHash));
280
+ clearTimeout(refetchIntervalTimeoutId.get(state.keyHash));
263
281
  if (typeof window !== 'undefined' && cacheTime !== Infinity) {
264
282
  resetTimeoutId.set(state.keyHash, window.setTimeout(() => {
265
283
  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.2",
4
4
  "description": "FloppyDisk - lightweight, simple, and powerful state management library",
5
5
  "keywords": [
6
6
  "state",