floppy-disk 2.2.0 → 2.3.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/README.md CHANGED
@@ -28,28 +28,39 @@ import { createQuery, createMutation } from 'floppy-disk'; // 8.4 kB (gzipped: 2
28
28
 
29
29
  ## Key Features
30
30
 
31
- - Create store ✅
32
- - Yes, it also support middleware & set/get state outside component
33
- - Store event (`onSubscribe`, `onUnsubscribe`, etc.)
34
- - Use store as local state manager ✅
35
- - Create stores (yes, it's plural) ✅ \*_controlled with a store key_
36
- - Create query
37
- - Dedupe multiple request
38
- - Auto-update stale data (stale-while-revalidate) ✅
39
- - Enable/disable query
40
- - Auto-fetch or manual (lazy query) ✅
41
- - Retry on error (retry count, retry delay)
42
- - SSR/SSG's initial query data ✅
43
- - Optimistic update ✅
44
- - Invalidate query
45
- - Reset query
46
- - Query with param (query key) ✅
47
- - Infinite query
48
- - Get query data outside component ✅
49
- - Custom reactivity
50
- - Fetching mechanisms are agnostically built on promises
51
- - Can be used with literally any asynchronous data fetching client, including GraphQL ✅
52
- - Create mutation ✅
31
+ - **Create Store**
32
+ - Get/set store inside/outside component
33
+ - Custom reactivity (like `useEffect`'s dependency array)
34
+ - Support middleware
35
+ - Set state interception
36
+ - Store event (`onSubscribe`, `onUnsubscribe`, etc.)
37
+ - Use store as local state manager
38
+ - **Create Stores**
39
+ - Same as store, but controlled with a store key
40
+ - **Create Query & Mutation**
41
+ - Backend agnostic (support GraphQL & any async function)
42
+ - TypeScript ready
43
+ - SSR/SSG support
44
+ - Custom reactivity (we choose when to re-render)
45
+ - **Create query**
46
+ - Dedupe multiple request
47
+ - Auto-fetch on mount or manual (lazy query)
48
+ - Enable/disable query
49
+ - Serve stale data while revalidating
50
+ - Retry on error (customizable)
51
+ - Optimistic update
52
+ - Invalidate query
53
+ - Reset query
54
+ - Query with param (query key)
55
+ - Paginated/infinite query
56
+ - Prefetch query
57
+ - Fetch from inside/outside component
58
+ - Get query state inside/outside component
59
+ - Suspense mode
60
+ - **Create mutation**
61
+ - Mutate from inside/outside component
62
+ - Get mutation state inside/outside component
63
+ - ... and [a lot more](https://floppy-disk.vercel.app/)
53
64
 
54
65
  <br>
55
66
 
@@ -661,10 +672,11 @@ function PokemonListPage() {
661
672
  **Note:**
662
673
 
663
674
  - The default stale time is 3 seconds.
664
- - The default error retry attempt is 1 time, and retry delay is 3 seconds.
665
- - The default reactivity of a query is `(s) => [s.data, s.error]`.
666
- (For paginated: `(s) => [s.data, s.error, s.isWaitingNextPage, s.hasNextPage]`)
667
- - You can change the `defaultDeps` on `createQuery` options.
675
+ - The default error retry attempt is 1 time, and retry delay is 2 seconds.
676
+ - The default reactivity of a query is:
677
+ `(s) => [s.data, s.error, s.isWaitingNextPage, s.hasNextPage]`
678
+ - Note that by default, subscribers don't listen to `isWaiting` state.
679
+ - You can change the `defaultDeps` on `createQuery` options.
668
680
 
669
681
  ### Mutation
670
682
 
@@ -12,7 +12,9 @@ export type MutationState<TVar, TResponse = any, TError = unknown> = {
12
12
  error: TError | null;
13
13
  errorUpdatedAt: number | null;
14
14
  /**
15
- * Mutate function is a promise that will always resolve.
15
+ * Mutate function.
16
+ *
17
+ * @returns Promise that will always get resolved.
16
18
  */
17
19
  mutate: TVar extends undefined ? () => Promise<{
18
20
  response?: TResponse;
@@ -1,3 +1,5 @@
1
+ /// <reference types="react" />
2
+ import { FunctionComponent } from 'preact';
1
3
  import { CreateStoresOptions, StoreKey, UseStores } from './create-stores';
2
4
  export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = {
3
5
  /**
@@ -14,8 +16,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
14
16
  fetch: () => void;
15
17
  /**
16
18
  * Will be called even if the data is still fresh (not stale).
19
+ *
20
+ * @returns Promise that will always get resolved.
17
21
  */
18
- forceFetch: () => void;
22
+ forceFetch: () => Promise<QueryState<TKey, TResponse, TData, TError>>;
19
23
  /**
20
24
  * Fetch next page if has next page.
21
25
  *
@@ -187,7 +191,7 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
187
191
  * IMPORTANT NOTE: Put this on the root component or parent component, before any component subscribed!
188
192
  */
189
193
  setInitialResponse: (options: {
190
- key?: TKey;
194
+ key?: TKey | null;
191
195
  response: TResponse;
192
196
  skipRevalidation?: boolean;
193
197
  }) => void;
@@ -221,5 +225,15 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
221
225
  revert: () => void;
222
226
  invalidate: () => void;
223
227
  };
228
+ /**
229
+ * Use query with suspense mode.
230
+ */
231
+ suspend: (key?: TKey | null) => QueryState<TKey, TResponse, TData, TError>;
232
+ Render: (props: {
233
+ queryKey?: TKey | null;
234
+ loading?: FunctionComponent<TKey>;
235
+ success?: FunctionComponent<TKey>;
236
+ error?: FunctionComponent<TKey>;
237
+ }) => JSX.Element;
224
238
  };
225
239
  export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => UseQuery<TKey, TResponse, TData, TError>;
@@ -1,3 +1,4 @@
1
+ import { h as createElement } from 'preact';
1
2
  import { useState } from 'preact/hooks';
2
3
  import { hasValue, identityFn, noop } from '../utils';
3
4
  import { createStores } from './create-stores';
@@ -56,7 +57,7 @@ export const createQuery = (queryFn, options = {}) => {
56
57
  const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
57
58
  return { shouldRetry, delay };
58
59
  };
59
- const forceFetch = () => {
60
+ const forceFetch = () => new Promise((resolve) => {
60
61
  const responseAllPages = [];
61
62
  const newPageParams = [undefined];
62
63
  let pageParam = undefined;
@@ -114,6 +115,7 @@ export const createQuery = (queryFn, options = {}) => {
114
115
  hasNextPage: hasValue(newPageParam),
115
116
  });
116
117
  onSuccess(response, stateBeforeCallQuery);
118
+ resolve(get());
117
119
  })
118
120
  .catch((error) => {
119
121
  const prevState = get();
@@ -147,20 +149,21 @@ export const createQuery = (queryFn, options = {}) => {
147
149
  pageParam,
148
150
  hasNextPage: hasValue(pageParam),
149
151
  });
150
- if (shouldRetry) {
152
+ if (shouldRetry && typeof window !== 'undefined') {
151
153
  retryTimeoutId.set(keyHash, window.setTimeout(() => {
152
154
  set({ retryCount: prevState.retryCount + 1 });
153
155
  callQuery();
154
156
  }, delay));
155
157
  }
156
158
  onError(error, stateBeforeCallQuery);
159
+ resolve(get());
157
160
  })
158
161
  .finally(() => {
159
162
  onSettled(stateBeforeCallQuery);
160
163
  });
161
164
  };
162
165
  callQuery();
163
- };
166
+ });
164
167
  const fetch = () => {
165
168
  const { responseUpdatedAt } = get();
166
169
  const isStale = Date.now() > Number(responseUpdatedAt) + staleTime;
@@ -341,5 +344,23 @@ export const createQuery = (queryFn, options = {}) => {
341
344
  const invalidate = () => useQuery.invalidateSpecificKey(key);
342
345
  return { revert, invalidate };
343
346
  };
347
+ useQuery.suspend = (key) => {
348
+ // eslint-disable-next-line react-hooks/rules-of-hooks
349
+ const state = useQuery(key);
350
+ if (state.isLoading)
351
+ throw state.forceFetch();
352
+ if (state.isError)
353
+ throw state.error;
354
+ return state;
355
+ };
356
+ const defaultElement = () => null;
357
+ useQuery.Render = (props) => {
358
+ const { queryKey, loading = defaultElement, success = defaultElement, error = defaultElement, } = props;
359
+ // eslint-disable-next-line react-hooks/rules-of-hooks
360
+ const state = useQuery(queryKey);
361
+ if (state.data)
362
+ return createElement(success, state.key);
363
+ return createElement(state.isLoading ? loading : error, state.key);
364
+ };
344
365
  return useQuery;
345
366
  };
@@ -12,7 +12,9 @@ export type MutationState<TVar, TResponse = any, TError = unknown> = {
12
12
  error: TError | null;
13
13
  errorUpdatedAt: number | null;
14
14
  /**
15
- * Mutate function is a promise that will always resolve.
15
+ * Mutate function.
16
+ *
17
+ * @returns Promise that will always get resolved.
16
18
  */
17
19
  mutate: TVar extends undefined ? () => Promise<{
18
20
  response?: TResponse;
@@ -1,3 +1,4 @@
1
+ import { FunctionComponent } from 'react';
1
2
  import { CreateStoresOptions, StoreKey, UseStores } from './create-stores';
2
3
  export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = {
3
4
  /**
@@ -14,8 +15,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
14
15
  fetch: () => void;
15
16
  /**
16
17
  * Will be called even if the data is still fresh (not stale).
18
+ *
19
+ * @returns Promise that will always get resolved.
17
20
  */
18
- forceFetch: () => void;
21
+ forceFetch: () => Promise<QueryState<TKey, TResponse, TData, TError>>;
19
22
  /**
20
23
  * Fetch next page if has next page.
21
24
  *
@@ -187,7 +190,7 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
187
190
  * IMPORTANT NOTE: Put this on the root component or parent component, before any component subscribed!
188
191
  */
189
192
  setInitialResponse: (options: {
190
- key?: TKey;
193
+ key?: TKey | null;
191
194
  response: TResponse;
192
195
  skipRevalidation?: boolean;
193
196
  }) => void;
@@ -221,5 +224,15 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
221
224
  revert: () => void;
222
225
  invalidate: () => void;
223
226
  };
227
+ /**
228
+ * Use query with suspense mode.
229
+ */
230
+ suspend: (key?: TKey | null) => QueryState<TKey, TResponse, TData, TError>;
231
+ Render: (props: {
232
+ queryKey?: TKey | null;
233
+ loading?: FunctionComponent<TKey>;
234
+ success?: FunctionComponent<TKey>;
235
+ error?: FunctionComponent<TKey>;
236
+ }) => JSX.Element;
224
237
  };
225
238
  export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => UseQuery<TKey, TResponse, TData, TError>;
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
1
+ import { createElement, useState } from 'react';
2
2
  import { hasValue, identityFn, noop } from '../utils';
3
3
  import { createStores } from './create-stores';
4
4
  const getDecision = (value, param, { ifTrue, ifAlways }) => {
@@ -56,7 +56,7 @@ export const createQuery = (queryFn, options = {}) => {
56
56
  const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
57
57
  return { shouldRetry, delay };
58
58
  };
59
- const forceFetch = () => {
59
+ const forceFetch = () => new Promise((resolve) => {
60
60
  const responseAllPages = [];
61
61
  const newPageParams = [undefined];
62
62
  let pageParam = undefined;
@@ -114,6 +114,7 @@ export const createQuery = (queryFn, options = {}) => {
114
114
  hasNextPage: hasValue(newPageParam),
115
115
  });
116
116
  onSuccess(response, stateBeforeCallQuery);
117
+ resolve(get());
117
118
  })
118
119
  .catch((error) => {
119
120
  const prevState = get();
@@ -147,20 +148,21 @@ export const createQuery = (queryFn, options = {}) => {
147
148
  pageParam,
148
149
  hasNextPage: hasValue(pageParam),
149
150
  });
150
- if (shouldRetry) {
151
+ if (shouldRetry && typeof window !== 'undefined') {
151
152
  retryTimeoutId.set(keyHash, window.setTimeout(() => {
152
153
  set({ retryCount: prevState.retryCount + 1 });
153
154
  callQuery();
154
155
  }, delay));
155
156
  }
156
157
  onError(error, stateBeforeCallQuery);
158
+ resolve(get());
157
159
  })
158
160
  .finally(() => {
159
161
  onSettled(stateBeforeCallQuery);
160
162
  });
161
163
  };
162
164
  callQuery();
163
- };
165
+ });
164
166
  const fetch = () => {
165
167
  const { responseUpdatedAt } = get();
166
168
  const isStale = Date.now() > Number(responseUpdatedAt) + staleTime;
@@ -341,5 +343,23 @@ export const createQuery = (queryFn, options = {}) => {
341
343
  const invalidate = () => useQuery.invalidateSpecificKey(key);
342
344
  return { revert, invalidate };
343
345
  };
346
+ useQuery.suspend = (key) => {
347
+ // eslint-disable-next-line react-hooks/rules-of-hooks
348
+ const state = useQuery(key);
349
+ if (state.isLoading)
350
+ throw state.forceFetch();
351
+ if (state.isError)
352
+ throw state.error;
353
+ return state;
354
+ };
355
+ const defaultElement = () => null;
356
+ useQuery.Render = (props) => {
357
+ const { queryKey, loading = defaultElement, success = defaultElement, error = defaultElement, } = props;
358
+ // eslint-disable-next-line react-hooks/rules-of-hooks
359
+ const state = useQuery(queryKey);
360
+ if (state.data)
361
+ return createElement(success, state.key);
362
+ return createElement(state.isLoading ? loading : error, state.key);
363
+ };
344
364
  return useQuery;
345
365
  };
@@ -12,7 +12,9 @@ export type MutationState<TVar, TResponse = any, TError = unknown> = {
12
12
  error: TError | null;
13
13
  errorUpdatedAt: number | null;
14
14
  /**
15
- * Mutate function is a promise that will always resolve.
15
+ * Mutate function.
16
+ *
17
+ * @returns Promise that will always get resolved.
16
18
  */
17
19
  mutate: TVar extends undefined ? () => Promise<{
18
20
  response?: TResponse;
@@ -1,3 +1,5 @@
1
+ /// <reference types="react" />
2
+ import { FunctionComponent } from 'preact';
1
3
  import { CreateStoresOptions, StoreKey, UseStores } from './create-stores';
2
4
  export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = {
3
5
  /**
@@ -14,8 +16,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
14
16
  fetch: () => void;
15
17
  /**
16
18
  * Will be called even if the data is still fresh (not stale).
19
+ *
20
+ * @returns Promise that will always get resolved.
17
21
  */
18
- forceFetch: () => void;
22
+ forceFetch: () => Promise<QueryState<TKey, TResponse, TData, TError>>;
19
23
  /**
20
24
  * Fetch next page if has next page.
21
25
  *
@@ -187,7 +191,7 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
187
191
  * IMPORTANT NOTE: Put this on the root component or parent component, before any component subscribed!
188
192
  */
189
193
  setInitialResponse: (options: {
190
- key?: TKey;
194
+ key?: TKey | null;
191
195
  response: TResponse;
192
196
  skipRevalidation?: boolean;
193
197
  }) => void;
@@ -221,5 +225,15 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
221
225
  revert: () => void;
222
226
  invalidate: () => void;
223
227
  };
228
+ /**
229
+ * Use query with suspense mode.
230
+ */
231
+ suspend: (key?: TKey | null) => QueryState<TKey, TResponse, TData, TError>;
232
+ Render: (props: {
233
+ queryKey?: TKey | null;
234
+ loading?: FunctionComponent<TKey>;
235
+ success?: FunctionComponent<TKey>;
236
+ error?: FunctionComponent<TKey>;
237
+ }) => JSX.Element;
224
238
  };
225
239
  export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => UseQuery<TKey, TResponse, TData, TError>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createQuery = void 0;
4
+ const preact_1 = require("preact");
4
5
  const hooks_1 = require("preact/hooks");
5
6
  const utils_1 = require("../utils");
6
7
  const create_stores_1 = require("./create-stores");
@@ -59,7 +60,7 @@ const createQuery = (queryFn, options = {}) => {
59
60
  const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
60
61
  return { shouldRetry, delay };
61
62
  };
62
- const forceFetch = () => {
63
+ const forceFetch = () => new Promise((resolve) => {
63
64
  const responseAllPages = [];
64
65
  const newPageParams = [undefined];
65
66
  let pageParam = undefined;
@@ -117,6 +118,7 @@ const createQuery = (queryFn, options = {}) => {
117
118
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
118
119
  });
119
120
  onSuccess(response, stateBeforeCallQuery);
121
+ resolve(get());
120
122
  })
121
123
  .catch((error) => {
122
124
  const prevState = get();
@@ -150,20 +152,21 @@ const createQuery = (queryFn, options = {}) => {
150
152
  pageParam,
151
153
  hasNextPage: (0, utils_1.hasValue)(pageParam),
152
154
  });
153
- if (shouldRetry) {
155
+ if (shouldRetry && typeof window !== 'undefined') {
154
156
  retryTimeoutId.set(keyHash, window.setTimeout(() => {
155
157
  set({ retryCount: prevState.retryCount + 1 });
156
158
  callQuery();
157
159
  }, delay));
158
160
  }
159
161
  onError(error, stateBeforeCallQuery);
162
+ resolve(get());
160
163
  })
161
164
  .finally(() => {
162
165
  onSettled(stateBeforeCallQuery);
163
166
  });
164
167
  };
165
168
  callQuery();
166
- };
169
+ });
167
170
  const fetch = () => {
168
171
  const { responseUpdatedAt } = get();
169
172
  const isStale = Date.now() > Number(responseUpdatedAt) + staleTime;
@@ -344,6 +347,24 @@ const createQuery = (queryFn, options = {}) => {
344
347
  const invalidate = () => useQuery.invalidateSpecificKey(key);
345
348
  return { revert, invalidate };
346
349
  };
350
+ useQuery.suspend = (key) => {
351
+ // eslint-disable-next-line react-hooks/rules-of-hooks
352
+ const state = useQuery(key);
353
+ if (state.isLoading)
354
+ throw state.forceFetch();
355
+ if (state.isError)
356
+ throw state.error;
357
+ return state;
358
+ };
359
+ const defaultElement = () => null;
360
+ useQuery.Render = (props) => {
361
+ const { queryKey, loading = defaultElement, success = defaultElement, error = defaultElement, } = props;
362
+ // eslint-disable-next-line react-hooks/rules-of-hooks
363
+ const state = useQuery(queryKey);
364
+ if (state.data)
365
+ return (0, preact_1.h)(success, state.key);
366
+ return (0, preact_1.h)(state.isLoading ? loading : error, state.key);
367
+ };
347
368
  return useQuery;
348
369
  };
349
370
  exports.createQuery = createQuery;
@@ -12,7 +12,9 @@ export type MutationState<TVar, TResponse = any, TError = unknown> = {
12
12
  error: TError | null;
13
13
  errorUpdatedAt: number | null;
14
14
  /**
15
- * Mutate function is a promise that will always resolve.
15
+ * Mutate function.
16
+ *
17
+ * @returns Promise that will always get resolved.
16
18
  */
17
19
  mutate: TVar extends undefined ? () => Promise<{
18
20
  response?: TResponse;
@@ -1,3 +1,4 @@
1
+ import { FunctionComponent } from 'react';
1
2
  import { CreateStoresOptions, StoreKey, UseStores } from './create-stores';
2
3
  export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown> = {
3
4
  /**
@@ -14,8 +15,10 @@ export type QueryState<TKey extends StoreKey = StoreKey, TResponse = any, TData
14
15
  fetch: () => void;
15
16
  /**
16
17
  * Will be called even if the data is still fresh (not stale).
18
+ *
19
+ * @returns Promise that will always get resolved.
17
20
  */
18
- forceFetch: () => void;
21
+ forceFetch: () => Promise<QueryState<TKey, TResponse, TData, TError>>;
19
22
  /**
20
23
  * Fetch next page if has next page.
21
24
  *
@@ -187,7 +190,7 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
187
190
  * IMPORTANT NOTE: Put this on the root component or parent component, before any component subscribed!
188
191
  */
189
192
  setInitialResponse: (options: {
190
- key?: TKey;
193
+ key?: TKey | null;
191
194
  response: TResponse;
192
195
  skipRevalidation?: boolean;
193
196
  }) => void;
@@ -221,5 +224,15 @@ export type UseQuery<TKey extends StoreKey = StoreKey, TResponse = any, TData =
221
224
  revert: () => void;
222
225
  invalidate: () => void;
223
226
  };
227
+ /**
228
+ * Use query with suspense mode.
229
+ */
230
+ suspend: (key?: TKey | null) => QueryState<TKey, TResponse, TData, TError>;
231
+ Render: (props: {
232
+ queryKey?: TKey | null;
233
+ loading?: FunctionComponent<TKey>;
234
+ success?: FunctionComponent<TKey>;
235
+ error?: FunctionComponent<TKey>;
236
+ }) => JSX.Element;
224
237
  };
225
238
  export declare const createQuery: <TKey extends StoreKey = StoreKey, TResponse = any, TData = TResponse, TError = unknown>(queryFn: (key: TKey, state: QueryState<TKey, TResponse, TData, TError>) => Promise<TResponse>, options?: CreateQueryOptions<TKey, TResponse, TData, TError>) => UseQuery<TKey, TResponse, TData, TError>;
@@ -59,7 +59,7 @@ const createQuery = (queryFn, options = {}) => {
59
59
  const delay = (typeof retryDelay === 'function' ? retryDelay(error, prevState) : retryDelay) || 0;
60
60
  return { shouldRetry, delay };
61
61
  };
62
- const forceFetch = () => {
62
+ const forceFetch = () => new Promise((resolve) => {
63
63
  const responseAllPages = [];
64
64
  const newPageParams = [undefined];
65
65
  let pageParam = undefined;
@@ -117,6 +117,7 @@ const createQuery = (queryFn, options = {}) => {
117
117
  hasNextPage: (0, utils_1.hasValue)(newPageParam),
118
118
  });
119
119
  onSuccess(response, stateBeforeCallQuery);
120
+ resolve(get());
120
121
  })
121
122
  .catch((error) => {
122
123
  const prevState = get();
@@ -150,20 +151,21 @@ const createQuery = (queryFn, options = {}) => {
150
151
  pageParam,
151
152
  hasNextPage: (0, utils_1.hasValue)(pageParam),
152
153
  });
153
- if (shouldRetry) {
154
+ if (shouldRetry && typeof window !== 'undefined') {
154
155
  retryTimeoutId.set(keyHash, window.setTimeout(() => {
155
156
  set({ retryCount: prevState.retryCount + 1 });
156
157
  callQuery();
157
158
  }, delay));
158
159
  }
159
160
  onError(error, stateBeforeCallQuery);
161
+ resolve(get());
160
162
  })
161
163
  .finally(() => {
162
164
  onSettled(stateBeforeCallQuery);
163
165
  });
164
166
  };
165
167
  callQuery();
166
- };
168
+ });
167
169
  const fetch = () => {
168
170
  const { responseUpdatedAt } = get();
169
171
  const isStale = Date.now() > Number(responseUpdatedAt) + staleTime;
@@ -344,6 +346,24 @@ const createQuery = (queryFn, options = {}) => {
344
346
  const invalidate = () => useQuery.invalidateSpecificKey(key);
345
347
  return { revert, invalidate };
346
348
  };
349
+ useQuery.suspend = (key) => {
350
+ // eslint-disable-next-line react-hooks/rules-of-hooks
351
+ const state = useQuery(key);
352
+ if (state.isLoading)
353
+ throw state.forceFetch();
354
+ if (state.isError)
355
+ throw state.error;
356
+ return state;
357
+ };
358
+ const defaultElement = () => null;
359
+ useQuery.Render = (props) => {
360
+ const { queryKey, loading = defaultElement, success = defaultElement, error = defaultElement, } = props;
361
+ // eslint-disable-next-line react-hooks/rules-of-hooks
362
+ const state = useQuery(queryKey);
363
+ if (state.data)
364
+ return (0, react_1.createElement)(success, state.key);
365
+ return (0, react_1.createElement)(state.isLoading ? loading : error, state.key);
366
+ };
347
367
  return useQuery;
348
368
  };
349
369
  exports.createQuery = createQuery;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floppy-disk",
3
- "version": "2.2.0",
3
+ "version": "2.3.0-beta.2",
4
4
  "description": "FloppyDisk - lightweight, simple, and powerful state management library",
5
5
  "keywords": [
6
6
  "state",