@suspensive/react-query-4 0.0.1-beta.0
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/LICENSE +21 -0
- package/dist/OmitKeyof-CFsZLI4k.d.cts +3 -0
- package/dist/OmitKeyof-CFsZLI4k.d.ts +3 -0
- package/dist/QueryErrorBoundary.cjs +81 -0
- package/dist/QueryErrorBoundary.cjs.map +1 -0
- package/dist/QueryErrorBoundary.d.cts +29 -0
- package/dist/QueryErrorBoundary.d.ts +29 -0
- package/dist/QueryErrorBoundary.js +9 -0
- package/dist/QueryErrorBoundary.js.map +1 -0
- package/dist/SuspenseInfiniteQuery.cjs +81 -0
- package/dist/SuspenseInfiniteQuery.cjs.map +1 -0
- package/dist/SuspenseInfiniteQuery.d.cts +14 -0
- package/dist/SuspenseInfiniteQuery.d.ts +14 -0
- package/dist/SuspenseInfiniteQuery.js +10 -0
- package/dist/SuspenseInfiniteQuery.js.map +1 -0
- package/dist/SuspenseQueries.cjs +69 -0
- package/dist/SuspenseQueries.cjs.map +1 -0
- package/dist/SuspenseQueries.d.cts +16 -0
- package/dist/SuspenseQueries.d.ts +16 -0
- package/dist/SuspenseQueries.js +10 -0
- package/dist/SuspenseQueries.js.map +1 -0
- package/dist/SuspenseQuery.cjs +81 -0
- package/dist/SuspenseQuery.cjs.map +1 -0
- package/dist/SuspenseQuery.d.cts +14 -0
- package/dist/SuspenseQuery.d.ts +14 -0
- package/dist/SuspenseQuery.js +10 -0
- package/dist/SuspenseQuery.js.map +1 -0
- package/dist/chunk-3B63MBHY.js +22 -0
- package/dist/chunk-3B63MBHY.js.map +1 -0
- package/dist/chunk-BN22WZP6.js +18 -0
- package/dist/chunk-BN22WZP6.js.map +1 -0
- package/dist/chunk-J3EHCRU6.js +20 -0
- package/dist/chunk-J3EHCRU6.js.map +1 -0
- package/dist/chunk-MEYWTEWA.js +11 -0
- package/dist/chunk-MEYWTEWA.js.map +1 -0
- package/dist/chunk-P5MROV72.js +34 -0
- package/dist/chunk-P5MROV72.js.map +1 -0
- package/dist/chunk-QETBZSG5.js +39 -0
- package/dist/chunk-QETBZSG5.js.map +1 -0
- package/dist/chunk-S7L2LUVS.js +22 -0
- package/dist/chunk-S7L2LUVS.js.map +1 -0
- package/dist/chunk-VPKMZV3T.js +23 -0
- package/dist/chunk-VPKMZV3T.js.map +1 -0
- package/dist/chunk-ZAOCTI7U.js +23 -0
- package/dist/chunk-ZAOCTI7U.js.map +1 -0
- package/dist/index.cjs +165 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/queryOptions.cjs +34 -0
- package/dist/queryOptions.cjs.map +1 -0
- package/dist/queryOptions.d.cts +17 -0
- package/dist/queryOptions.d.ts +17 -0
- package/dist/queryOptions.js +9 -0
- package/dist/queryOptions.js.map +1 -0
- package/dist/useSuspenseInfiniteQuery.cjs +56 -0
- package/dist/useSuspenseInfiniteQuery.cjs.map +1 -0
- package/dist/useSuspenseInfiniteQuery.d.cts +16 -0
- package/dist/useSuspenseInfiniteQuery.d.ts +16 -0
- package/dist/useSuspenseInfiniteQuery.js +9 -0
- package/dist/useSuspenseInfiniteQuery.js.map +1 -0
- package/dist/useSuspenseQueries.cjs +58 -0
- package/dist/useSuspenseQueries.cjs.map +1 -0
- package/dist/useSuspenseQueries.d.cts +55 -0
- package/dist/useSuspenseQueries.d.ts +55 -0
- package/dist/useSuspenseQueries.js +9 -0
- package/dist/useSuspenseQueries.js.map +1 -0
- package/dist/useSuspenseQuery.cjs +56 -0
- package/dist/useSuspenseQuery.cjs.map +1 -0
- package/dist/useSuspenseQuery.d.cts +16 -0
- package/dist/useSuspenseQuery.d.ts +16 -0
- package/dist/useSuspenseQuery.js +9 -0
- package/dist/useSuspenseQuery.js.map +1 -0
- package/package.json +80 -0
- package/src/QueryErrorBoundary.tsx +27 -0
- package/src/SuspenseInfiniteQuery.test-d.tsx +102 -0
- package/src/SuspenseInfiniteQuery.tsx +22 -0
- package/src/SuspenseQueries.test-d.tsx +54 -0
- package/src/SuspenseQueries.tsx +15 -0
- package/src/SuspenseQuery.test-d.tsx +94 -0
- package/src/SuspenseQuery.tsx +18 -0
- package/src/index.ts +13 -0
- package/src/queryOptions.test-d.tsx +119 -0
- package/src/queryOptions.ts +88 -0
- package/src/useSuspenseInfiniteQuery.test-d.ts +59 -0
- package/src/useSuspenseInfiniteQuery.ts +47 -0
- package/src/useSuspenseQueries.test-d.ts +75 -0
- package/src/useSuspenseQueries.ts +127 -0
- package/src/useSuspenseQuery.test-d.ts +51 -0
- package/src/useSuspenseQuery.ts +36 -0
- package/src/utility-types/OmitKeyof.test-d.ts +175 -0
- package/src/utility-types/OmitKeyof.ts +11 -0
- package/src/utility-types/RequiredKeyof.ts +3 -0
- package/src/utility-types/index.ts +2 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { QueryKey } from '@tanstack/react-query'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import {
|
|
4
|
+
type UseSuspenseInfiniteQueryOptions,
|
|
5
|
+
type UseSuspenseInfiniteQueryResult,
|
|
6
|
+
useSuspenseInfiniteQuery,
|
|
7
|
+
} from './useSuspenseInfiniteQuery'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @experimental This is experimental feature.
|
|
11
|
+
*/
|
|
12
|
+
export const SuspenseInfiniteQuery = <
|
|
13
|
+
TQueryFnData = unknown,
|
|
14
|
+
TError = unknown,
|
|
15
|
+
TData = TQueryFnData,
|
|
16
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
17
|
+
>({
|
|
18
|
+
children,
|
|
19
|
+
...options
|
|
20
|
+
}: UseSuspenseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
|
|
21
|
+
children: (query: UseSuspenseInfiniteQueryResult<TData, TError>) => ReactNode
|
|
22
|
+
}) => <>{children(useSuspenseInfiniteQuery(options))}</>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { queryFn, queryKey } from '@suspensive/test-utils'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
4
|
+
import { SuspenseQueries } from './SuspenseQueries'
|
|
5
|
+
import type { UseSuspenseQueryResult } from './useSuspenseQuery'
|
|
6
|
+
|
|
7
|
+
describe('<SuspenseQueries/>', () => {
|
|
8
|
+
it('type check', () => {
|
|
9
|
+
;() => (
|
|
10
|
+
<SuspenseQueries queries={[{ queryKey, queryFn }]}>
|
|
11
|
+
{([
|
|
12
|
+
query1,
|
|
13
|
+
// @ts-expect-error Tuple type '[UseSuspenseQueryResult<{ text: string; }, unknown>]' of length '1' has no element at index '1'.
|
|
14
|
+
query2,
|
|
15
|
+
// @ts-expect-error Tuple type '[UseSuspenseQueryResult<{ text: string; }, unknown>]' of length '1' has no element at index '2'.
|
|
16
|
+
query3,
|
|
17
|
+
]) => {
|
|
18
|
+
expectTypeOf(query1).toEqualTypeOf<UseSuspenseQueryResult<{ text: string }>>()
|
|
19
|
+
expectTypeOf(query2).toEqualTypeOf<undefined>()
|
|
20
|
+
expectTypeOf(query3).toEqualTypeOf<undefined>()
|
|
21
|
+
return <></>
|
|
22
|
+
}}
|
|
23
|
+
</SuspenseQueries>
|
|
24
|
+
)
|
|
25
|
+
;() => (
|
|
26
|
+
<SuspenseQueries
|
|
27
|
+
queries={[
|
|
28
|
+
{ queryKey, queryFn },
|
|
29
|
+
{ queryKey, queryFn },
|
|
30
|
+
]}
|
|
31
|
+
>
|
|
32
|
+
{([
|
|
33
|
+
query1,
|
|
34
|
+
|
|
35
|
+
query2,
|
|
36
|
+
// @ts-expect-error Tuple type '[UseSuspenseQueryResult<{ text: string; }, unknown>]' of length '1' has no element at index '2'.
|
|
37
|
+
query3,
|
|
38
|
+
]) => {
|
|
39
|
+
expectTypeOf(query1).toEqualTypeOf<UseSuspenseQueryResult<{ text: string }>>()
|
|
40
|
+
expectTypeOf(query2).toEqualTypeOf<UseSuspenseQueryResult<{ text: string }>>()
|
|
41
|
+
expectTypeOf(query3).toEqualTypeOf<undefined>()
|
|
42
|
+
return <></>
|
|
43
|
+
}}
|
|
44
|
+
</SuspenseQueries>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
expectTypeOf(
|
|
48
|
+
<SuspenseQueries queries={[{ queryKey, queryFn }]}>{() => <></>}</SuspenseQueries>
|
|
49
|
+
).toEqualTypeOf<JSX.Element>()
|
|
50
|
+
expectTypeOf(
|
|
51
|
+
<SuspenseQueries queries={[{ queryKey, queryFn }]}>{() => <></>}</SuspenseQueries>
|
|
52
|
+
).not.toEqualTypeOf<ReactNode>()
|
|
53
|
+
})
|
|
54
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { type SuspenseQueriesOptions, type SuspenseQueriesResults, useSuspenseQueries } from './useSuspenseQueries'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @experimental This is experimental feature.
|
|
6
|
+
*/
|
|
7
|
+
export function SuspenseQueries<T extends any[]>({
|
|
8
|
+
children,
|
|
9
|
+
queries,
|
|
10
|
+
}: {
|
|
11
|
+
queries: readonly [...SuspenseQueriesOptions<T>]
|
|
12
|
+
children: (queries: SuspenseQueriesResults<T>) => ReactNode
|
|
13
|
+
}) {
|
|
14
|
+
return <>{children(useSuspenseQueries({ queries }))}</>
|
|
15
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { queryFn, queryKey } from '@suspensive/test-utils'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
4
|
+
import { SuspenseQuery } from './SuspenseQuery'
|
|
5
|
+
import type { UseSuspenseQueryResult } from './useSuspenseQuery'
|
|
6
|
+
|
|
7
|
+
describe('<SuspenseQuery/>', () => {
|
|
8
|
+
it('type error', () => {
|
|
9
|
+
;() => (
|
|
10
|
+
<SuspenseQuery
|
|
11
|
+
queryKey={queryKey}
|
|
12
|
+
queryFn={queryFn}
|
|
13
|
+
//@ts-expect-error no suspense
|
|
14
|
+
suspense={boolean}
|
|
15
|
+
>
|
|
16
|
+
{(query) => <>{query.data.text}</>}
|
|
17
|
+
</SuspenseQuery>
|
|
18
|
+
)
|
|
19
|
+
;() => (
|
|
20
|
+
<SuspenseQuery
|
|
21
|
+
queryKey={queryKey}
|
|
22
|
+
queryFn={queryFn}
|
|
23
|
+
//@ts-expect-error no useErrorBoundary
|
|
24
|
+
useErrorBoundary={boolean}
|
|
25
|
+
>
|
|
26
|
+
{(query) => <>{query.data.text}</>}
|
|
27
|
+
</SuspenseQuery>
|
|
28
|
+
)
|
|
29
|
+
;() => (
|
|
30
|
+
<SuspenseQuery
|
|
31
|
+
queryKey={queryKey}
|
|
32
|
+
queryFn={queryFn}
|
|
33
|
+
//@ts-expect-error no enabled
|
|
34
|
+
enabled={boolean}
|
|
35
|
+
>
|
|
36
|
+
{(query) => <>{query.data.text}</>}
|
|
37
|
+
</SuspenseQuery>
|
|
38
|
+
)
|
|
39
|
+
;() => (
|
|
40
|
+
<SuspenseQuery
|
|
41
|
+
queryKey={queryKey}
|
|
42
|
+
queryFn={queryFn}
|
|
43
|
+
//@ts-expect-error no placeholderData
|
|
44
|
+
placeholderData="placeholder"
|
|
45
|
+
>
|
|
46
|
+
{(query) => <>{query.data.text}</>}
|
|
47
|
+
</SuspenseQuery>
|
|
48
|
+
)
|
|
49
|
+
;() => (
|
|
50
|
+
<SuspenseQuery
|
|
51
|
+
queryKey={queryKey}
|
|
52
|
+
queryFn={queryFn}
|
|
53
|
+
//@ts-expect-error no placeholderData
|
|
54
|
+
placeholderData="placeholder"
|
|
55
|
+
>
|
|
56
|
+
{(query) => <>{query.data.text}</>}
|
|
57
|
+
</SuspenseQuery>
|
|
58
|
+
)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('type check', () => {
|
|
62
|
+
;() => (
|
|
63
|
+
<SuspenseQuery queryKey={queryKey} queryFn={queryFn}>
|
|
64
|
+
{(query) => {
|
|
65
|
+
expectTypeOf(query).toEqualTypeOf<UseSuspenseQueryResult<{ text: string }>>()
|
|
66
|
+
expectTypeOf(query.data).toEqualTypeOf<{ text: string }>()
|
|
67
|
+
expectTypeOf(query.status).toEqualTypeOf<'success'>()
|
|
68
|
+
return <></>
|
|
69
|
+
}}
|
|
70
|
+
</SuspenseQuery>
|
|
71
|
+
)
|
|
72
|
+
;() => (
|
|
73
|
+
<SuspenseQuery queryKey={queryKey} queryFn={queryFn} select={(data) => data.text}>
|
|
74
|
+
{(selectedQuery) => {
|
|
75
|
+
expectTypeOf(selectedQuery).toEqualTypeOf<UseSuspenseQueryResult<string>>()
|
|
76
|
+
expectTypeOf(selectedQuery.data).toEqualTypeOf<string>()
|
|
77
|
+
expectTypeOf(selectedQuery.status).toEqualTypeOf<'success'>()
|
|
78
|
+
return <></>
|
|
79
|
+
}}
|
|
80
|
+
</SuspenseQuery>
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
expectTypeOf(
|
|
84
|
+
<SuspenseQuery queryKey={queryKey} queryFn={queryFn}>
|
|
85
|
+
{() => <></>}
|
|
86
|
+
</SuspenseQuery>
|
|
87
|
+
).toEqualTypeOf<JSX.Element>()
|
|
88
|
+
expectTypeOf(
|
|
89
|
+
<SuspenseQuery queryKey={queryKey} queryFn={queryFn}>
|
|
90
|
+
{() => <></>}
|
|
91
|
+
</SuspenseQuery>
|
|
92
|
+
).not.toEqualTypeOf<ReactNode>()
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { QueryKey } from '@tanstack/react-query'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import { type UseSuspenseQueryOptions, type UseSuspenseQueryResult, useSuspenseQuery } from './useSuspenseQuery'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @experimental This is experimental feature.
|
|
7
|
+
*/
|
|
8
|
+
export const SuspenseQuery = <
|
|
9
|
+
TQueryFnData = unknown,
|
|
10
|
+
TError = unknown,
|
|
11
|
+
TData = TQueryFnData,
|
|
12
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
13
|
+
>({
|
|
14
|
+
children,
|
|
15
|
+
...options
|
|
16
|
+
}: UseSuspenseQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
|
|
17
|
+
children: (queryResult: UseSuspenseQueryResult<TData, TError>) => ReactNode
|
|
18
|
+
}) => <>{children(useSuspenseQuery(options))}</>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { queryOptions } from './queryOptions'
|
|
2
|
+
export { SuspenseQuery } from './SuspenseQuery'
|
|
3
|
+
export { SuspenseQueries } from './SuspenseQueries'
|
|
4
|
+
export { SuspenseInfiniteQuery } from './SuspenseInfiniteQuery'
|
|
5
|
+
|
|
6
|
+
export { useSuspenseQuery } from './useSuspenseQuery'
|
|
7
|
+
export type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from './useSuspenseQuery'
|
|
8
|
+
export { useSuspenseQueries } from './useSuspenseQueries'
|
|
9
|
+
export type { SuspenseQueriesOptions, SuspenseQueriesResults } from './useSuspenseQueries'
|
|
10
|
+
export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery'
|
|
11
|
+
export type { UseSuspenseInfiniteQueryOptions, UseSuspenseInfiniteQueryResult } from './useSuspenseInfiniteQuery'
|
|
12
|
+
|
|
13
|
+
export { QueryErrorBoundary } from './QueryErrorBoundary'
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { queryKey } from '@suspensive/test-utils'
|
|
2
|
+
import { type UseQueryResult, useQueries, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
3
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
4
|
+
import { queryOptions } from './queryOptions'
|
|
5
|
+
import { SuspenseQuery } from './SuspenseQuery'
|
|
6
|
+
import { useSuspenseQueries } from './useSuspenseQueries'
|
|
7
|
+
import { type UseSuspenseQueryResult, useSuspenseQuery } from './useSuspenseQuery'
|
|
8
|
+
|
|
9
|
+
const query = {
|
|
10
|
+
options1: () =>
|
|
11
|
+
queryOptions({
|
|
12
|
+
queryKey: [...queryKey, 1] as const,
|
|
13
|
+
queryFn: () => Promise.resolve({ field: 'success' }),
|
|
14
|
+
}),
|
|
15
|
+
options2: () =>
|
|
16
|
+
queryOptions({
|
|
17
|
+
queryKey: [...queryKey, 2] as const,
|
|
18
|
+
queryFn: () => Promise.resolve({ field: 'success' }),
|
|
19
|
+
}),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('queryOptions', () => {
|
|
23
|
+
it('should be used with useQuery', () => {
|
|
24
|
+
const keyFn1Query = useQuery(query.options1())
|
|
25
|
+
expectTypeOf(keyFn1Query).toEqualTypeOf<UseQueryResult<{ field: string }>>()
|
|
26
|
+
expectTypeOf(keyFn1Query.data).toEqualTypeOf<{ field: string } | undefined>()
|
|
27
|
+
const keyFn1Query_Select = useQuery({ ...query.options1(), select: (data) => data.field })
|
|
28
|
+
expectTypeOf(keyFn1Query_Select).toEqualTypeOf<UseQueryResult<string>>()
|
|
29
|
+
expectTypeOf(keyFn1Query_Select.data).toEqualTypeOf<string | undefined>()
|
|
30
|
+
})
|
|
31
|
+
it('should be used with useSuspenseQuery', () => {
|
|
32
|
+
const keyFn1SuspenseQuery = useSuspenseQuery(query.options1())
|
|
33
|
+
expectTypeOf(keyFn1SuspenseQuery).toEqualTypeOf<UseSuspenseQueryResult<{ field: string }>>()
|
|
34
|
+
expectTypeOf(keyFn1SuspenseQuery.data).toEqualTypeOf<{ field: string }>()
|
|
35
|
+
const keyFn1SuspenseQuery_Select = useSuspenseQuery({ ...query.options1(), select: (data) => data.field })
|
|
36
|
+
expectTypeOf(keyFn1SuspenseQuery_Select).toEqualTypeOf<UseSuspenseQueryResult<string>>()
|
|
37
|
+
expectTypeOf(keyFn1SuspenseQuery_Select.data).toEqualTypeOf<string>()
|
|
38
|
+
})
|
|
39
|
+
it('should be used with <SuspenseQuery/>', () => {
|
|
40
|
+
;(() => (
|
|
41
|
+
<SuspenseQuery {...query.options1()}>
|
|
42
|
+
{(keyFn1SuspenseQuery) => {
|
|
43
|
+
expectTypeOf(keyFn1SuspenseQuery).toEqualTypeOf<UseSuspenseQueryResult<{ field: string }>>()
|
|
44
|
+
expectTypeOf(keyFn1SuspenseQuery.data).toEqualTypeOf<{ field: string }>()
|
|
45
|
+
return <></>
|
|
46
|
+
}}
|
|
47
|
+
</SuspenseQuery>
|
|
48
|
+
))()
|
|
49
|
+
;(() => (
|
|
50
|
+
<SuspenseQuery {...query.options1()} select={(data) => data.field}>
|
|
51
|
+
{(keyFn1SuspenseQuery_Select) => {
|
|
52
|
+
expectTypeOf(keyFn1SuspenseQuery_Select).toEqualTypeOf<UseSuspenseQueryResult<string>>()
|
|
53
|
+
expectTypeOf(keyFn1SuspenseQuery_Select.data).toEqualTypeOf<string>()
|
|
54
|
+
return <></>
|
|
55
|
+
}}
|
|
56
|
+
</SuspenseQuery>
|
|
57
|
+
))()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should be used with useQueries', () => {
|
|
61
|
+
const [query1, query2, query3] = useQueries({
|
|
62
|
+
queries: [
|
|
63
|
+
query.options1(),
|
|
64
|
+
{ ...query.options2() },
|
|
65
|
+
queryOptions({
|
|
66
|
+
queryKey: [...queryKey, 4] as const,
|
|
67
|
+
queryFn: () => Promise.resolve({ field: 'success' }),
|
|
68
|
+
select: (data) => {
|
|
69
|
+
expectTypeOf(data).toEqualTypeOf<{ field: string }>()
|
|
70
|
+
return data.field
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
],
|
|
74
|
+
})
|
|
75
|
+
expectTypeOf(query1).toEqualTypeOf<UseQueryResult<{ field: string }>>()
|
|
76
|
+
expectTypeOf(query1.data).toEqualTypeOf<{ field: string } | undefined>()
|
|
77
|
+
expectTypeOf(query2).toEqualTypeOf<UseQueryResult<{ field: string }>>()
|
|
78
|
+
expectTypeOf(query2.data).toEqualTypeOf<{ field: string } | undefined>()
|
|
79
|
+
expectTypeOf(query3).toEqualTypeOf<UseQueryResult<string>>()
|
|
80
|
+
expectTypeOf(query3.data).toEqualTypeOf<string | undefined>()
|
|
81
|
+
})
|
|
82
|
+
it('should be used with useSuspenseQueries', () => {
|
|
83
|
+
const [query1, query2, query3] = useSuspenseQueries({
|
|
84
|
+
queries: [
|
|
85
|
+
query.options1(),
|
|
86
|
+
{ ...query.options2() },
|
|
87
|
+
queryOptions({
|
|
88
|
+
queryKey: [...queryKey, 4] as const,
|
|
89
|
+
queryFn: () => Promise.resolve({ field: 'success' }),
|
|
90
|
+
select: (data) => {
|
|
91
|
+
expectTypeOf(data).toEqualTypeOf<{ field: string }>()
|
|
92
|
+
return data.field
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
],
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
expectTypeOf(query1).toEqualTypeOf<UseSuspenseQueryResult<{ field: string }>>()
|
|
99
|
+
expectTypeOf(query1.data).toEqualTypeOf<{ field: string }>()
|
|
100
|
+
expectTypeOf(query2).toEqualTypeOf<UseSuspenseQueryResult<{ field: string }>>()
|
|
101
|
+
expectTypeOf(query2.data).toEqualTypeOf<{ field: string }>()
|
|
102
|
+
expectTypeOf(query3).toEqualTypeOf<UseSuspenseQueryResult<string>>()
|
|
103
|
+
expectTypeOf(query3.data).toEqualTypeOf<string>()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should be used with useQueryClient', async () => {
|
|
107
|
+
const queryClient = useQueryClient()
|
|
108
|
+
|
|
109
|
+
queryClient.invalidateQueries(query.options1())
|
|
110
|
+
queryClient.resetQueries(query.options1())
|
|
111
|
+
queryClient.removeQueries(query.options1())
|
|
112
|
+
queryClient.cancelQueries(query.options1())
|
|
113
|
+
queryClient.prefetchQuery(query.options1())
|
|
114
|
+
queryClient.refetchQueries(query.options1())
|
|
115
|
+
|
|
116
|
+
const query1 = await queryClient.fetchQuery(query.options1())
|
|
117
|
+
expectTypeOf(query1).toEqualTypeOf<{ field: string }>()
|
|
118
|
+
})
|
|
119
|
+
})
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { QueryKey, UseQueryOptions } from '@tanstack/react-query'
|
|
2
|
+
import type { OmitKeyof, RequiredKeyof } from './utility-types'
|
|
3
|
+
|
|
4
|
+
type SelectedQueryOptions<
|
|
5
|
+
TQueryFnData = unknown,
|
|
6
|
+
TError = unknown,
|
|
7
|
+
TData = TQueryFnData,
|
|
8
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
9
|
+
> = RequiredKeyof<
|
|
10
|
+
OmitKeyof<
|
|
11
|
+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
|
12
|
+
| 'getNextPageParam'
|
|
13
|
+
| 'getPreviousPageParam'
|
|
14
|
+
| 'queryKeyHashFn'
|
|
15
|
+
| '_defaulted'
|
|
16
|
+
| 'behavior'
|
|
17
|
+
| 'structuralSharing'
|
|
18
|
+
| 'isDataEqual'
|
|
19
|
+
| 'onSuccess'
|
|
20
|
+
| 'onError'
|
|
21
|
+
| 'onSettled'
|
|
22
|
+
| 'enabled'
|
|
23
|
+
| 'refetchInterval'
|
|
24
|
+
| 'initialData'
|
|
25
|
+
>,
|
|
26
|
+
'queryKey' | 'queryFn'
|
|
27
|
+
> & {
|
|
28
|
+
select: (data: TQueryFnData) => TData
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type UnSelectedQueryOptions<
|
|
32
|
+
TQueryFnData = unknown,
|
|
33
|
+
TError = unknown,
|
|
34
|
+
TData = TQueryFnData,
|
|
35
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
36
|
+
> = RequiredKeyof<
|
|
37
|
+
OmitKeyof<
|
|
38
|
+
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
|
39
|
+
| 'getNextPageParam'
|
|
40
|
+
| 'getPreviousPageParam'
|
|
41
|
+
| 'queryKeyHashFn'
|
|
42
|
+
| '_defaulted'
|
|
43
|
+
| 'behavior'
|
|
44
|
+
| 'structuralSharing'
|
|
45
|
+
| 'isDataEqual'
|
|
46
|
+
| 'onSuccess'
|
|
47
|
+
| 'onError'
|
|
48
|
+
| 'onSettled'
|
|
49
|
+
| 'enabled'
|
|
50
|
+
| 'refetchInterval'
|
|
51
|
+
| 'initialData'
|
|
52
|
+
>,
|
|
53
|
+
'queryKey' | 'queryFn'
|
|
54
|
+
> & {
|
|
55
|
+
select?: undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function queryOptions<
|
|
59
|
+
TQueryFnData = unknown,
|
|
60
|
+
TError = unknown,
|
|
61
|
+
TData = TQueryFnData,
|
|
62
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
63
|
+
>(
|
|
64
|
+
options: SelectedQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
65
|
+
): SelectedQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
66
|
+
export function queryOptions<
|
|
67
|
+
TQueryFnData = unknown,
|
|
68
|
+
TError = unknown,
|
|
69
|
+
TData = TQueryFnData,
|
|
70
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
71
|
+
>(
|
|
72
|
+
options: UnSelectedQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
73
|
+
): UnSelectedQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
74
|
+
/**
|
|
75
|
+
* @experimental This is experimental feature.
|
|
76
|
+
*/
|
|
77
|
+
export function queryOptions<
|
|
78
|
+
TQueryFnData = unknown,
|
|
79
|
+
TError = unknown,
|
|
80
|
+
TData = TQueryFnData,
|
|
81
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
82
|
+
>(
|
|
83
|
+
options:
|
|
84
|
+
| SelectedQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
85
|
+
| UnSelectedQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
86
|
+
) {
|
|
87
|
+
return options
|
|
88
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { queryFn, queryKey } from '@suspensive/test-utils'
|
|
2
|
+
import type { InfiniteData } from '@tanstack/react-query'
|
|
3
|
+
import { type UseSuspenseInfiniteQueryResult, useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery'
|
|
4
|
+
|
|
5
|
+
describe('useSuspenseInfiniteQuery', () => {
|
|
6
|
+
it('type error', () => {
|
|
7
|
+
// @ts-expect-error no arg
|
|
8
|
+
useSuspenseInfiniteQuery()
|
|
9
|
+
|
|
10
|
+
useSuspenseInfiniteQuery({
|
|
11
|
+
queryKey,
|
|
12
|
+
queryFn,
|
|
13
|
+
// @ts-expect-error no suspense
|
|
14
|
+
suspense: boolean,
|
|
15
|
+
})
|
|
16
|
+
useSuspenseInfiniteQuery({
|
|
17
|
+
queryKey,
|
|
18
|
+
queryFn,
|
|
19
|
+
// @ts-expect-error no useErrorBoundary
|
|
20
|
+
useErrorBoundary: boolean,
|
|
21
|
+
})
|
|
22
|
+
useSuspenseInfiniteQuery({
|
|
23
|
+
queryKey,
|
|
24
|
+
queryFn,
|
|
25
|
+
// @ts-expect-error no enabled
|
|
26
|
+
enabled: boolean,
|
|
27
|
+
})
|
|
28
|
+
useSuspenseInfiniteQuery({
|
|
29
|
+
queryKey,
|
|
30
|
+
queryFn,
|
|
31
|
+
// @ts-expect-error no placeholderData
|
|
32
|
+
placeholderData: 'placeholder',
|
|
33
|
+
})
|
|
34
|
+
useSuspenseInfiniteQuery({
|
|
35
|
+
queryKey,
|
|
36
|
+
queryFn,
|
|
37
|
+
// @ts-expect-error no isPlaceholderData
|
|
38
|
+
}).isPlaceholderData
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('type check', () => {
|
|
42
|
+
const infiniteQuery = useSuspenseInfiniteQuery({ queryKey, queryFn })
|
|
43
|
+
expectTypeOf(infiniteQuery).toEqualTypeOf<UseSuspenseInfiniteQueryResult<{ text: string }>>()
|
|
44
|
+
expectTypeOf(infiniteQuery.data).toEqualTypeOf<InfiniteData<{ text: string }>>()
|
|
45
|
+
expectTypeOf(infiniteQuery.status).toEqualTypeOf<'success'>()
|
|
46
|
+
|
|
47
|
+
const selectedInfiniteQuery = useSuspenseInfiniteQuery({
|
|
48
|
+
queryKey,
|
|
49
|
+
queryFn,
|
|
50
|
+
select: (data) => ({
|
|
51
|
+
pages: data.pages.map(({ text }) => text),
|
|
52
|
+
pageParams: data.pageParams,
|
|
53
|
+
}),
|
|
54
|
+
})
|
|
55
|
+
expectTypeOf(selectedInfiniteQuery).toEqualTypeOf<UseSuspenseInfiniteQueryResult<string>>()
|
|
56
|
+
expectTypeOf(selectedInfiniteQuery.data).toEqualTypeOf<InfiniteData<string>>()
|
|
57
|
+
expectTypeOf(selectedInfiniteQuery.status).toEqualTypeOf<'success'>()
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type InfiniteData,
|
|
3
|
+
type QueryKey,
|
|
4
|
+
type UseInfiniteQueryOptions,
|
|
5
|
+
type UseInfiniteQueryResult,
|
|
6
|
+
useInfiniteQuery,
|
|
7
|
+
} from '@tanstack/react-query'
|
|
8
|
+
import type { OmitKeyof } from './utility-types'
|
|
9
|
+
|
|
10
|
+
export interface UseSuspenseInfiniteQueryResult<TData = unknown, TError = unknown>
|
|
11
|
+
extends OmitKeyof<
|
|
12
|
+
UseInfiniteQueryResult<TData, TError>,
|
|
13
|
+
keyof Pick<UseInfiniteQueryResult<TData, TError>, 'isPlaceholderData'>
|
|
14
|
+
> {
|
|
15
|
+
data: InfiniteData<TData>
|
|
16
|
+
status: 'success'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UseSuspenseInfiniteQueryOptions<
|
|
20
|
+
TQueryFnData = unknown,
|
|
21
|
+
TError = unknown,
|
|
22
|
+
TData = TQueryFnData,
|
|
23
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
24
|
+
> extends OmitKeyof<
|
|
25
|
+
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
|
|
26
|
+
'suspense' | 'useErrorBoundary' | 'enabled' | 'placeholderData'
|
|
27
|
+
> {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* This hook is wrapping useInfiniteQuery of `@tanstack/react-query` v4 with default suspense option.
|
|
31
|
+
* @see {@link https://suspensive.org/docs/react-query/useSuspenseInfiniteQuery}
|
|
32
|
+
*/
|
|
33
|
+
export function useSuspenseInfiniteQuery<
|
|
34
|
+
TQueryFnData = unknown,
|
|
35
|
+
TError = unknown,
|
|
36
|
+
TData = TQueryFnData,
|
|
37
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
38
|
+
>(
|
|
39
|
+
options: UseSuspenseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
|
40
|
+
): UseSuspenseInfiniteQueryResult<TData, TError> {
|
|
41
|
+
return useInfiniteQuery({
|
|
42
|
+
...options,
|
|
43
|
+
enabled: true,
|
|
44
|
+
suspense: true,
|
|
45
|
+
useErrorBoundary: true,
|
|
46
|
+
}) as UseSuspenseInfiniteQueryResult<TData, TError>
|
|
47
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { queryFn, queryKey, select } from '@suspensive/test-utils'
|
|
2
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
3
|
+
import { queryOptions } from './queryOptions'
|
|
4
|
+
import { useSuspenseQueries } from './useSuspenseQueries'
|
|
5
|
+
import type { UseSuspenseQueryResult } from './useSuspenseQuery'
|
|
6
|
+
|
|
7
|
+
describe('useSuspenseQueries', () => {
|
|
8
|
+
it('type error', () => {
|
|
9
|
+
useSuspenseQueries({
|
|
10
|
+
queries: [
|
|
11
|
+
{
|
|
12
|
+
queryKey: [...queryKey, 1] as const,
|
|
13
|
+
queryFn,
|
|
14
|
+
// @ts-expect-error no suspense
|
|
15
|
+
suspense: false,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
queryKey: [...queryKey, 2] as const,
|
|
19
|
+
queryFn,
|
|
20
|
+
select,
|
|
21
|
+
// @ts-expect-error no suspense
|
|
22
|
+
suspense: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
queryKey: [...queryKey, 3] as const,
|
|
26
|
+
queryFn,
|
|
27
|
+
// @ts-expect-error no enabled
|
|
28
|
+
enabled: true,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
queryKey: [...queryKey, 4] as const,
|
|
32
|
+
queryFn,
|
|
33
|
+
// @ts-expect-error no enabled
|
|
34
|
+
enabled: true,
|
|
35
|
+
select,
|
|
36
|
+
},
|
|
37
|
+
queryOptions({
|
|
38
|
+
queryKey: [...queryKey, 4] as const,
|
|
39
|
+
queryFn: () => Promise.resolve({ field: 'success' }),
|
|
40
|
+
select: (data) => data.field,
|
|
41
|
+
}),
|
|
42
|
+
] as const,
|
|
43
|
+
})
|
|
44
|
+
// @ts-expect-error if no items
|
|
45
|
+
useSuspenseQueries({})
|
|
46
|
+
// @ts-expect-error if no items
|
|
47
|
+
useSuspenseQueries()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('type check', () => {
|
|
51
|
+
const [query, selectedQuery, selectedQueryByQueryOptions] = useSuspenseQueries({
|
|
52
|
+
queries: [
|
|
53
|
+
{ queryKey: [...queryKey, 5] as const, queryFn },
|
|
54
|
+
{ queryKey: [...queryKey, 6] as const, queryFn, select },
|
|
55
|
+
queryOptions({
|
|
56
|
+
queryKey: [...queryKey, 4] as const,
|
|
57
|
+
queryFn: () => Promise.resolve({ field: 'success' }),
|
|
58
|
+
select: (data) => data.field,
|
|
59
|
+
}),
|
|
60
|
+
] as const,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expectTypeOf(query).toEqualTypeOf<UseSuspenseQueryResult<{ text: string }>>()
|
|
64
|
+
expectTypeOf(query.status).toEqualTypeOf<`success`>()
|
|
65
|
+
expectTypeOf(query.data).toEqualTypeOf<{ text: string }>()
|
|
66
|
+
|
|
67
|
+
expectTypeOf(selectedQuery).toEqualTypeOf<UseSuspenseQueryResult<string>>()
|
|
68
|
+
expectTypeOf(selectedQuery.status).toEqualTypeOf<`success`>()
|
|
69
|
+
expectTypeOf(selectedQuery.data).toEqualTypeOf<string>()
|
|
70
|
+
|
|
71
|
+
expectTypeOf(selectedQueryByQueryOptions).toEqualTypeOf<UseSuspenseQueryResult<string>>()
|
|
72
|
+
expectTypeOf(selectedQueryByQueryOptions.status).toEqualTypeOf<`success`>()
|
|
73
|
+
expectTypeOf(selectedQueryByQueryOptions.data).toEqualTypeOf<string>()
|
|
74
|
+
})
|
|
75
|
+
})
|