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 +38 -26
- package/esm/preact/create-mutation.d.ts +3 -1
- package/esm/preact/create-query.d.ts +16 -2
- package/esm/preact/create-query.js +24 -3
- package/esm/react/create-mutation.d.ts +3 -1
- package/esm/react/create-query.d.ts +15 -2
- package/esm/react/create-query.js +24 -4
- package/lib/preact/create-mutation.d.ts +3 -1
- package/lib/preact/create-query.d.ts +16 -2
- package/lib/preact/create-query.js +24 -3
- package/lib/react/create-mutation.d.ts +3 -1
- package/lib/react/create-query.d.ts +15 -2
- package/lib/react/create-query.js +23 -3
- package/package.json +1 -1
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
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
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
|
|
665
|
-
- The default reactivity of a query is
|
|
666
|
-
|
|
667
|
-
-
|
|
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
|
|
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: () =>
|
|
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
|
|
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: () =>
|
|
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
|
|
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: () =>
|
|
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
|
|
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: () =>
|
|
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;
|