@tanstack/react-query 5.22.2 → 5.24.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/build/codemods/coverage/clover.xml +2 -2
- package/build/codemods/coverage/index.html +1 -1
- package/build/codemods/coverage/utils/index.html +1 -1
- package/build/codemods/coverage/utils/index.js.html +1 -1
- package/build/codemods/coverage/utils/transformers/index.html +1 -1
- package/build/codemods/coverage/utils/transformers/query-cache-transformer.js.html +1 -1
- package/build/codemods/coverage/utils/transformers/query-client-transformer.js.html +1 -1
- package/build/codemods/coverage/utils/transformers/use-query-like-transformer.js.html +1 -1
- package/build/codemods/coverage/v4/index.html +1 -1
- package/build/codemods/coverage/v4/key-transformation.js.html +1 -1
- package/build/codemods/coverage/v4/replace-import-specifier.js.html +1 -1
- package/build/codemods/coverage/v4/utils/replacers/index.html +1 -1
- package/build/codemods/coverage/v4/utils/replacers/key-replacer.js.html +1 -1
- package/build/codemods/coverage/v5/is-loading/index.html +1 -1
- package/build/codemods/coverage/v5/is-loading/is-loading.js.html +1 -1
- package/build/codemods/coverage/v5/keep-previous-data/index.html +1 -1
- package/build/codemods/coverage/v5/keep-previous-data/keep-previous-data.js.html +1 -1
- package/build/codemods/coverage/v5/keep-previous-data/utils/already-has-placeholder-data-property.js.html +1 -1
- package/build/codemods/coverage/v5/keep-previous-data/utils/index.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/index.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/remove-overloads.js.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/transformers/filter-aware-usage-transformer.js.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/transformers/index.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/transformers/query-fn-aware-usage-transformer.js.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/utils/index.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/utils/index.js.html +1 -1
- package/build/codemods/coverage/v5/remove-overloads/utils/unknown-usage-error.js.html +1 -1
- package/build/codemods/coverage/v5/rename-hydrate/index.html +1 -1
- package/build/codemods/coverage/v5/rename-hydrate/rename-hydrate.js.html +1 -1
- package/build/codemods/coverage/v5/rename-properties/index.html +1 -1
- package/build/codemods/coverage/v5/rename-properties/rename-properties.js.html +1 -1
- package/build/legacy/suspense.cjs +1 -1
- package/build/legacy/suspense.cjs.map +1 -1
- package/build/legacy/suspense.js +1 -1
- package/build/legacy/suspense.js.map +1 -1
- package/build/legacy/useMutation.cjs +1 -3
- package/build/legacy/useMutation.cjs.map +1 -1
- package/build/legacy/useMutation.js +1 -3
- package/build/legacy/useMutation.js.map +1 -1
- package/build/legacy/useSuspenseQueries.cjs +2 -1
- package/build/legacy/useSuspenseQueries.cjs.map +1 -1
- package/build/legacy/useSuspenseQueries.js +2 -1
- package/build/legacy/useSuspenseQueries.js.map +1 -1
- package/build/legacy/useSuspenseQuery.cjs +2 -1
- package/build/legacy/useSuspenseQuery.cjs.map +1 -1
- package/build/legacy/useSuspenseQuery.js +2 -1
- package/build/legacy/useSuspenseQuery.js.map +1 -1
- package/build/legacy/utils.cjs +4 -0
- package/build/legacy/utils.cjs.map +1 -1
- package/build/legacy/utils.d.cts +2 -1
- package/build/legacy/utils.d.ts +2 -1
- package/build/legacy/utils.js +3 -0
- package/build/legacy/utils.js.map +1 -1
- package/build/modern/suspense.cjs +1 -1
- package/build/modern/suspense.cjs.map +1 -1
- package/build/modern/suspense.js +1 -1
- package/build/modern/suspense.js.map +1 -1
- package/build/modern/useMutation.cjs +1 -3
- package/build/modern/useMutation.cjs.map +1 -1
- package/build/modern/useMutation.js +1 -3
- package/build/modern/useMutation.js.map +1 -1
- package/build/modern/useSuspenseQueries.cjs +2 -1
- package/build/modern/useSuspenseQueries.cjs.map +1 -1
- package/build/modern/useSuspenseQueries.js +2 -1
- package/build/modern/useSuspenseQueries.js.map +1 -1
- package/build/modern/useSuspenseQuery.cjs +2 -1
- package/build/modern/useSuspenseQuery.cjs.map +1 -1
- package/build/modern/useSuspenseQuery.js +2 -1
- package/build/modern/useSuspenseQuery.js.map +1 -1
- package/build/modern/utils.cjs +4 -0
- package/build/modern/utils.cjs.map +1 -1
- package/build/modern/utils.d.cts +2 -1
- package/build/modern/utils.d.ts +2 -1
- package/build/modern/utils.js +3 -0
- package/build/modern/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/infiniteQueryOptions.test-d.tsx +142 -0
- package/src/__tests__/queryOptions.test-d.tsx +140 -0
- package/src/__tests__/suspense.test-d.tsx +124 -0
- package/src/__tests__/suspense.test.tsx +102 -0
- package/src/__tests__/useInfiniteQuery.test-d.tsx +140 -0
- package/src/__tests__/useMutation.test.tsx +3 -1
- package/src/__tests__/useQueries.test-d.tsx +126 -0
- package/src/__tests__/useQuery.test-d.tsx +140 -0
- package/src/__tests__/utils.tsx +0 -8
- package/src/suspense.ts +1 -1
- package/src/useMutation.ts +1 -3
- package/src/useSuspenseQueries.ts +1 -0
- package/src/useSuspenseQuery.ts +1 -0
- package/src/utils.ts +2 -0
- package/src/__tests__/infiniteQueryOptions.types.test.tsx +0 -177
- package/src/__tests__/queryOptions.types.test.tsx +0 -192
- package/src/__tests__/suspense.types.test.tsx +0 -147
- package/src/__tests__/useInfiniteQuery.type.test.tsx +0 -224
- package/src/__tests__/useQueries.types.test.tsx +0 -148
- package/src/__tests__/useQuery.types.test.tsx +0 -179
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, expect, expectTypeOf, it } from 'vitest'
|
|
2
|
+
import { QueryClient } from '@tanstack/query-core'
|
|
3
|
+
import { dataTagSymbol } from '@tanstack/query-core'
|
|
4
|
+
import { queryOptions } from '../queryOptions'
|
|
5
|
+
import { useQuery } from '../useQuery'
|
|
6
|
+
import { useQueries } from '../useQueries'
|
|
7
|
+
import { useSuspenseQuery } from '../useSuspenseQuery'
|
|
8
|
+
|
|
9
|
+
describe('queryOptions', () => {
|
|
10
|
+
it('should not allow excess properties', () => {
|
|
11
|
+
queryOptions({
|
|
12
|
+
queryKey: ['key'],
|
|
13
|
+
queryFn: () => Promise.resolve(5),
|
|
14
|
+
// @ts-expect-error this is a good error, because stallTime does not exist!
|
|
15
|
+
stallTime: 1000,
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
it('should infer types for callbacks', () => {
|
|
19
|
+
queryOptions({
|
|
20
|
+
queryKey: ['key'],
|
|
21
|
+
queryFn: () => Promise.resolve(5),
|
|
22
|
+
staleTime: 1000,
|
|
23
|
+
select: (data) => {
|
|
24
|
+
expectTypeOf(data).toEqualTypeOf<number>()
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
it('should work when passed to useQuery', () => {
|
|
29
|
+
const options = queryOptions({
|
|
30
|
+
queryKey: ['key'],
|
|
31
|
+
queryFn: () => Promise.resolve(5),
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const { data } = useQuery(options)
|
|
35
|
+
expectTypeOf(data).toEqualTypeOf<number | undefined>()
|
|
36
|
+
})
|
|
37
|
+
it('should work when passed to useSuspenseQuery', () => {
|
|
38
|
+
const options = queryOptions({
|
|
39
|
+
queryKey: ['key'],
|
|
40
|
+
queryFn: () => Promise.resolve(5),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const { data } = useSuspenseQuery(options)
|
|
44
|
+
expectTypeOf(data).toEqualTypeOf<number>()
|
|
45
|
+
})
|
|
46
|
+
it('should work when passed to fetchQuery', async () => {
|
|
47
|
+
const options = queryOptions({
|
|
48
|
+
queryKey: ['key'],
|
|
49
|
+
queryFn: () => Promise.resolve(5),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const data = await new QueryClient().fetchQuery(options)
|
|
53
|
+
expectTypeOf(data).toEqualTypeOf<number>()
|
|
54
|
+
})
|
|
55
|
+
it('should work when passed to useQueries', () => {
|
|
56
|
+
const options = queryOptions({
|
|
57
|
+
queryKey: ['key'],
|
|
58
|
+
queryFn: () => Promise.resolve(5),
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const [{ data }] = useQueries({
|
|
62
|
+
queries: [options],
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
expectTypeOf(data).toEqualTypeOf<number | undefined>()
|
|
66
|
+
})
|
|
67
|
+
it('should tag the queryKey with the result type of the QueryFn', () => {
|
|
68
|
+
expect(() => {
|
|
69
|
+
const { queryKey } = queryOptions({
|
|
70
|
+
queryKey: ['key'],
|
|
71
|
+
queryFn: () => Promise.resolve(5),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
it('should tag the queryKey even if no promise is returned', () => {
|
|
78
|
+
const { queryKey } = queryOptions({
|
|
79
|
+
queryKey: ['key'],
|
|
80
|
+
queryFn: () => 5,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
|
|
84
|
+
})
|
|
85
|
+
it('should tag the queryKey with unknown if there is no queryFn', () => {
|
|
86
|
+
const { queryKey } = queryOptions({
|
|
87
|
+
queryKey: ['key'],
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<unknown>()
|
|
91
|
+
})
|
|
92
|
+
it('should tag the queryKey with the result type of the QueryFn if select is used', () => {
|
|
93
|
+
const { queryKey } = queryOptions({
|
|
94
|
+
queryKey: ['key'],
|
|
95
|
+
queryFn: () => Promise.resolve(5),
|
|
96
|
+
select: (data) => data.toString(),
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expectTypeOf(queryKey[dataTagSymbol]).toEqualTypeOf<number>()
|
|
100
|
+
})
|
|
101
|
+
it('should return the proper type when passed to getQueryData', () => {
|
|
102
|
+
const { queryKey } = queryOptions({
|
|
103
|
+
queryKey: ['key'],
|
|
104
|
+
queryFn: () => Promise.resolve(5),
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const queryClient = new QueryClient()
|
|
108
|
+
const data = queryClient.getQueryData(queryKey)
|
|
109
|
+
expectTypeOf(data).toEqualTypeOf<number | undefined>()
|
|
110
|
+
})
|
|
111
|
+
it('should properly type updaterFn when passed to setQueryData', () => {
|
|
112
|
+
const { queryKey } = queryOptions({
|
|
113
|
+
queryKey: ['key'],
|
|
114
|
+
queryFn: () => Promise.resolve(5),
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const queryClient = new QueryClient()
|
|
118
|
+
const data = queryClient.setQueryData(queryKey, (prev) => {
|
|
119
|
+
expectTypeOf(prev).toEqualTypeOf<number | undefined>()
|
|
120
|
+
return prev
|
|
121
|
+
})
|
|
122
|
+
expectTypeOf(data).toEqualTypeOf<number | undefined>()
|
|
123
|
+
})
|
|
124
|
+
it('should properly type value when passed to setQueryData', () => {
|
|
125
|
+
const { queryKey } = queryOptions({
|
|
126
|
+
queryKey: ['key'],
|
|
127
|
+
queryFn: () => Promise.resolve(5),
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const queryClient = new QueryClient()
|
|
131
|
+
|
|
132
|
+
// @ts-expect-error value should be a number
|
|
133
|
+
queryClient.setQueryData(queryKey, '5')
|
|
134
|
+
// @ts-expect-error value should be a number
|
|
135
|
+
queryClient.setQueryData(queryKey, () => '5')
|
|
136
|
+
|
|
137
|
+
const data = queryClient.setQueryData(queryKey, 5)
|
|
138
|
+
expectTypeOf(data).toEqualTypeOf<number | undefined>()
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
2
|
+
import { useSuspenseQuery } from '../useSuspenseQuery'
|
|
3
|
+
import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
|
|
4
|
+
import type { InfiniteData } from '@tanstack/query-core'
|
|
5
|
+
|
|
6
|
+
describe('useSuspenseQuery', () => {
|
|
7
|
+
it('should always have data defined', () => {
|
|
8
|
+
const { data } = useSuspenseQuery({
|
|
9
|
+
queryKey: ['key'],
|
|
10
|
+
queryFn: () => Promise.resolve(5),
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
expectTypeOf(data).toEqualTypeOf<number>()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should not have pending status', () => {
|
|
17
|
+
const { status } = useSuspenseQuery({
|
|
18
|
+
queryKey: ['key'],
|
|
19
|
+
queryFn: () => Promise.resolve(5),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
expectTypeOf(status).toEqualTypeOf<'error' | 'success'>()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should not allow placeholderData, enabled or throwOnError props', () => {
|
|
26
|
+
useSuspenseQuery({
|
|
27
|
+
queryKey: ['key'],
|
|
28
|
+
queryFn: () => Promise.resolve(5),
|
|
29
|
+
// @ts-expect-error TS2345
|
|
30
|
+
placeholderData: 5,
|
|
31
|
+
enabled: true,
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
useSuspenseQuery({
|
|
35
|
+
queryKey: ['key'],
|
|
36
|
+
queryFn: () => Promise.resolve(5),
|
|
37
|
+
// @ts-expect-error TS2345
|
|
38
|
+
enabled: true,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
useSuspenseQuery({
|
|
42
|
+
queryKey: ['key'],
|
|
43
|
+
queryFn: () => Promise.resolve(5),
|
|
44
|
+
// @ts-expect-error TS2345
|
|
45
|
+
throwOnError: true,
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should not return isPlaceholderData', () => {
|
|
50
|
+
const query = useSuspenseQuery({
|
|
51
|
+
queryKey: ['key'],
|
|
52
|
+
queryFn: () => Promise.resolve(5),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// @ts-expect-error TS2339
|
|
56
|
+
query.isPlaceholderData
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('useSuspenseInfiniteQuery', () => {
|
|
61
|
+
it('should always have data defined', () => {
|
|
62
|
+
const { data } = useSuspenseInfiniteQuery({
|
|
63
|
+
queryKey: ['key'],
|
|
64
|
+
queryFn: () => Promise.resolve(5),
|
|
65
|
+
initialPageParam: 1,
|
|
66
|
+
getNextPageParam: () => 1,
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
expectTypeOf(data).toEqualTypeOf<InfiniteData<number, unknown>>()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should not have pending status', () => {
|
|
73
|
+
const { status } = useSuspenseInfiniteQuery({
|
|
74
|
+
queryKey: ['key'],
|
|
75
|
+
queryFn: () => Promise.resolve(5),
|
|
76
|
+
initialPageParam: 1,
|
|
77
|
+
getNextPageParam: () => 1,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
expectTypeOf(status).toEqualTypeOf<'error' | 'success'>()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should not allow placeholderData, enabled or throwOnError props', () => {
|
|
84
|
+
useSuspenseInfiniteQuery({
|
|
85
|
+
queryKey: ['key'],
|
|
86
|
+
queryFn: () => Promise.resolve(5),
|
|
87
|
+
initialPageParam: 1,
|
|
88
|
+
getNextPageParam: () => 1,
|
|
89
|
+
// @ts-expect-error TS2345
|
|
90
|
+
placeholderData: 5,
|
|
91
|
+
enabled: true,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
useSuspenseInfiniteQuery({
|
|
95
|
+
queryKey: ['key'],
|
|
96
|
+
queryFn: () => Promise.resolve(5),
|
|
97
|
+
initialPageParam: 1,
|
|
98
|
+
getNextPageParam: () => 1,
|
|
99
|
+
// @ts-expect-error TS2345
|
|
100
|
+
enabled: true,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
useSuspenseInfiniteQuery({
|
|
104
|
+
queryKey: ['key'],
|
|
105
|
+
queryFn: () => Promise.resolve(5),
|
|
106
|
+
initialPageParam: 1,
|
|
107
|
+
getNextPageParam: () => 1,
|
|
108
|
+
// @ts-expect-error TS2345
|
|
109
|
+
throwOnError: true,
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should not return isPlaceholderData', () => {
|
|
114
|
+
const query = useSuspenseInfiniteQuery({
|
|
115
|
+
queryKey: ['key'],
|
|
116
|
+
queryFn: () => Promise.resolve(5),
|
|
117
|
+
initialPageParam: 1,
|
|
118
|
+
getNextPageParam: () => 1,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// @ts-expect-error TS2339
|
|
122
|
+
query.isPlaceholderData
|
|
123
|
+
})
|
|
124
|
+
})
|
|
@@ -779,6 +779,58 @@ describe('useSuspenseQuery', () => {
|
|
|
779
779
|
|
|
780
780
|
consoleMock.mockRestore()
|
|
781
781
|
})
|
|
782
|
+
|
|
783
|
+
it('should still suspense if queryClient has placeholderData config', async () => {
|
|
784
|
+
const key = queryKey()
|
|
785
|
+
const queryClientWithPlaceholder = createQueryClient({
|
|
786
|
+
defaultOptions: {
|
|
787
|
+
queries: {
|
|
788
|
+
placeholderData: (previousData: any) => previousData,
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
})
|
|
792
|
+
const states: Array<UseSuspenseQueryResult<number>> = []
|
|
793
|
+
|
|
794
|
+
let count = 0
|
|
795
|
+
let renders = 0
|
|
796
|
+
|
|
797
|
+
function Page() {
|
|
798
|
+
renders++
|
|
799
|
+
|
|
800
|
+
const [stateKey, setStateKey] = React.useState(key)
|
|
801
|
+
|
|
802
|
+
const state = useSuspenseQuery({
|
|
803
|
+
queryKey: stateKey,
|
|
804
|
+
queryFn: async () => {
|
|
805
|
+
count++
|
|
806
|
+
await sleep(100)
|
|
807
|
+
return count
|
|
808
|
+
},
|
|
809
|
+
})
|
|
810
|
+
|
|
811
|
+
states.push(state)
|
|
812
|
+
|
|
813
|
+
return (
|
|
814
|
+
<div>
|
|
815
|
+
<button aria-label="toggle" onClick={() => setStateKey(queryKey())} />
|
|
816
|
+
data: {String(state.data)}
|
|
817
|
+
</div>
|
|
818
|
+
)
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
const rendered = renderWithClient(
|
|
822
|
+
queryClientWithPlaceholder,
|
|
823
|
+
<React.Suspense fallback="loading">
|
|
824
|
+
<Page />
|
|
825
|
+
</React.Suspense>,
|
|
826
|
+
)
|
|
827
|
+
await waitFor(() => rendered.getByText('loading'))
|
|
828
|
+
await waitFor(() => rendered.getByText('data: 1'))
|
|
829
|
+
fireEvent.click(rendered.getByLabelText('toggle'))
|
|
830
|
+
|
|
831
|
+
await waitFor(() => rendered.getByText('loading'))
|
|
832
|
+
await waitFor(() => rendered.getByText('data: 2'))
|
|
833
|
+
})
|
|
782
834
|
})
|
|
783
835
|
|
|
784
836
|
describe('useSuspenseQueries', () => {
|
|
@@ -1090,4 +1142,54 @@ describe('useSuspenseQueries', () => {
|
|
|
1090
1142
|
|
|
1091
1143
|
expect(queryFnCount).toBe(2)
|
|
1092
1144
|
})
|
|
1145
|
+
|
|
1146
|
+
it('should still suspense if queryClient has placeholderData config', async () => {
|
|
1147
|
+
const key = queryKey()
|
|
1148
|
+
const queryClientWithPlaceholder = createQueryClient({
|
|
1149
|
+
defaultOptions: {
|
|
1150
|
+
queries: {
|
|
1151
|
+
placeholderData: (previousData: any) => previousData,
|
|
1152
|
+
},
|
|
1153
|
+
},
|
|
1154
|
+
})
|
|
1155
|
+
|
|
1156
|
+
function Page() {
|
|
1157
|
+
const [count, setCount] = React.useState(0)
|
|
1158
|
+
const [isPending, startTransition] = React.useTransition()
|
|
1159
|
+
const { data } = useSuspenseQuery({
|
|
1160
|
+
queryKey: [key, count],
|
|
1161
|
+
queryFn: async () => {
|
|
1162
|
+
await sleep(10)
|
|
1163
|
+
return 'data' + count
|
|
1164
|
+
},
|
|
1165
|
+
})
|
|
1166
|
+
|
|
1167
|
+
return (
|
|
1168
|
+
<div>
|
|
1169
|
+
<button onClick={() => startTransition(() => setCount(count + 1))}>
|
|
1170
|
+
inc
|
|
1171
|
+
</button>
|
|
1172
|
+
|
|
1173
|
+
<div>{isPending ? 'Pending...' : String(data)}</div>
|
|
1174
|
+
</div>
|
|
1175
|
+
)
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
const rendered = renderWithClient(
|
|
1179
|
+
queryClientWithPlaceholder,
|
|
1180
|
+
<React.Suspense fallback={'Loading...'}>
|
|
1181
|
+
<Page />
|
|
1182
|
+
</React.Suspense>,
|
|
1183
|
+
)
|
|
1184
|
+
|
|
1185
|
+
await waitFor(() => rendered.getByText('Loading...'))
|
|
1186
|
+
|
|
1187
|
+
await waitFor(() => rendered.getByText('data0'))
|
|
1188
|
+
|
|
1189
|
+
fireEvent.click(rendered.getByText('inc'))
|
|
1190
|
+
|
|
1191
|
+
await waitFor(() => rendered.getByText('Pending...'))
|
|
1192
|
+
|
|
1193
|
+
await waitFor(() => rendered.getByText('data1'))
|
|
1194
|
+
})
|
|
1093
1195
|
})
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
2
|
+
import { QueryClient } from '@tanstack/query-core'
|
|
3
|
+
import { useInfiniteQuery } from '../useInfiniteQuery'
|
|
4
|
+
import { useQuery } from '../useQuery'
|
|
5
|
+
import type { InfiniteData } from '@tanstack/query-core'
|
|
6
|
+
|
|
7
|
+
describe('pageParam', () => {
|
|
8
|
+
it('initialPageParam should define type of param passed to queryFunctionContext', () => {
|
|
9
|
+
useInfiniteQuery({
|
|
10
|
+
queryKey: ['key'],
|
|
11
|
+
queryFn: ({ pageParam }) => {
|
|
12
|
+
expectTypeOf(pageParam).toEqualTypeOf<number>()
|
|
13
|
+
},
|
|
14
|
+
initialPageParam: 1,
|
|
15
|
+
getNextPageParam: () => undefined,
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('direction should be passed to queryFn of useInfiniteQuery', () => {
|
|
20
|
+
useInfiniteQuery({
|
|
21
|
+
queryKey: ['key'],
|
|
22
|
+
queryFn: ({ direction }) => {
|
|
23
|
+
expectTypeOf(direction).toEqualTypeOf<'forward' | 'backward'>()
|
|
24
|
+
},
|
|
25
|
+
initialPageParam: 1,
|
|
26
|
+
getNextPageParam: () => undefined,
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('there should be no pageParam passed to the queryFn of useQuery', () => {
|
|
31
|
+
useQuery({
|
|
32
|
+
queryKey: ['key'],
|
|
33
|
+
// @ts-expect-error there should be no pageParam passed to queryFn of useQuery
|
|
34
|
+
queryFn: ({ pageParam }) => {
|
|
35
|
+
return String(pageParam)
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('there should be no direction passed to the queryFn of useQuery', () => {
|
|
41
|
+
useQuery({
|
|
42
|
+
queryKey: ['key'],
|
|
43
|
+
// @ts-expect-error there should be no pageParam passed to queryFn of useQuery
|
|
44
|
+
queryFn: ({ direction }) => {
|
|
45
|
+
return String(direction)
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('initialPageParam should define type of param passed to queryFunctionContext for fetchInfiniteQuery', () => {
|
|
51
|
+
const queryClient = new QueryClient()
|
|
52
|
+
queryClient.fetchInfiniteQuery({
|
|
53
|
+
queryKey: ['key'],
|
|
54
|
+
queryFn: ({ pageParam }) => {
|
|
55
|
+
expectTypeOf(pageParam).toEqualTypeOf<number>()
|
|
56
|
+
},
|
|
57
|
+
initialPageParam: 1,
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('initialPageParam should define type of param passed to queryFunctionContext for prefetchInfiniteQuery', () => {
|
|
62
|
+
const queryClient = new QueryClient()
|
|
63
|
+
queryClient.prefetchInfiniteQuery({
|
|
64
|
+
queryKey: ['key'],
|
|
65
|
+
queryFn: ({ pageParam }) => {
|
|
66
|
+
expectTypeOf(pageParam).toEqualTypeOf<number>()
|
|
67
|
+
},
|
|
68
|
+
initialPageParam: 1,
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
describe('select', () => {
|
|
73
|
+
it('should still return paginated data if no select result', () => {
|
|
74
|
+
const infiniteQuery = useInfiniteQuery({
|
|
75
|
+
queryKey: ['key'],
|
|
76
|
+
queryFn: ({ pageParam }) => {
|
|
77
|
+
return pageParam * 5
|
|
78
|
+
},
|
|
79
|
+
initialPageParam: 1,
|
|
80
|
+
getNextPageParam: () => undefined,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// TODO: Order of generics prevents pageParams to be typed correctly. Using `unknown` for now
|
|
84
|
+
expectTypeOf(infiniteQuery.data).toEqualTypeOf<
|
|
85
|
+
InfiniteData<number, unknown> | undefined
|
|
86
|
+
>()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should be able to transform data to arbitrary result', () => {
|
|
90
|
+
const infiniteQuery = useInfiniteQuery({
|
|
91
|
+
queryKey: ['key'],
|
|
92
|
+
queryFn: ({ pageParam }) => {
|
|
93
|
+
return pageParam * 5
|
|
94
|
+
},
|
|
95
|
+
initialPageParam: 1,
|
|
96
|
+
getNextPageParam: () => undefined,
|
|
97
|
+
select: (data) => {
|
|
98
|
+
expectTypeOf(data).toEqualTypeOf<InfiniteData<number, number>>()
|
|
99
|
+
return 'selected' as const
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expectTypeOf(infiniteQuery.data).toEqualTypeOf<'selected' | undefined>()
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
describe('getNextPageParam / getPreviousPageParam', () => {
|
|
107
|
+
it('should get typed params', () => {
|
|
108
|
+
const infiniteQuery = useInfiniteQuery({
|
|
109
|
+
queryKey: ['key'],
|
|
110
|
+
queryFn: ({ pageParam }) => {
|
|
111
|
+
return String(pageParam)
|
|
112
|
+
},
|
|
113
|
+
initialPageParam: 1,
|
|
114
|
+
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
|
|
115
|
+
expectTypeOf(lastPage).toEqualTypeOf<string>()
|
|
116
|
+
expectTypeOf(allPages).toEqualTypeOf<Array<string>>()
|
|
117
|
+
expectTypeOf(lastPageParam).toEqualTypeOf<number>()
|
|
118
|
+
expectTypeOf(allPageParams).toEqualTypeOf<Array<number>>()
|
|
119
|
+
return undefined
|
|
120
|
+
},
|
|
121
|
+
getPreviousPageParam: (
|
|
122
|
+
firstPage,
|
|
123
|
+
allPages,
|
|
124
|
+
firstPageParam,
|
|
125
|
+
allPageParams,
|
|
126
|
+
) => {
|
|
127
|
+
expectTypeOf(firstPage).toEqualTypeOf<string>()
|
|
128
|
+
expectTypeOf(allPages).toEqualTypeOf<Array<string>>()
|
|
129
|
+
expectTypeOf(firstPageParam).toEqualTypeOf<number>()
|
|
130
|
+
expectTypeOf(allPageParams).toEqualTypeOf<Array<number>>()
|
|
131
|
+
return undefined
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// TODO: Order of generics prevents pageParams to be typed correctly. Using `unknown` for now
|
|
136
|
+
expectTypeOf(infiniteQuery.data).toEqualTypeOf<
|
|
137
|
+
InfiniteData<string, unknown> | undefined
|
|
138
|
+
>()
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -1138,6 +1138,8 @@ describe('useMutation', () => {
|
|
|
1138
1138
|
|
|
1139
1139
|
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
1140
1140
|
|
|
1141
|
-
await
|
|
1141
|
+
await waitFor(() =>
|
|
1142
|
+
rendered.findByText('data: custom client, status: success'),
|
|
1143
|
+
)
|
|
1142
1144
|
})
|
|
1143
1145
|
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
2
|
+
import { queryOptions } from '../queryOptions'
|
|
3
|
+
import { useQueries } from '../useQueries'
|
|
4
|
+
import type { UseQueryOptions } from '../types'
|
|
5
|
+
|
|
6
|
+
describe('UseQueries config object overload', () => {
|
|
7
|
+
it('TData should always be defined when initialData is provided as an object', () => {
|
|
8
|
+
const query1 = {
|
|
9
|
+
queryKey: ['key1'],
|
|
10
|
+
queryFn: () => {
|
|
11
|
+
return {
|
|
12
|
+
wow: true,
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
initialData: {
|
|
16
|
+
wow: false,
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const query2 = {
|
|
21
|
+
queryKey: ['key2'],
|
|
22
|
+
queryFn: () => 'Query Data',
|
|
23
|
+
initialData: 'initial data',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const query3 = {
|
|
27
|
+
queryKey: ['key2'],
|
|
28
|
+
queryFn: () => 'Query Data',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const queryResults = useQueries({ queries: [query1, query2, query3] })
|
|
32
|
+
|
|
33
|
+
const query1Data = queryResults[0].data
|
|
34
|
+
const query2Data = queryResults[1].data
|
|
35
|
+
const query3Data = queryResults[2].data
|
|
36
|
+
|
|
37
|
+
expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>()
|
|
38
|
+
expectTypeOf(query2Data).toEqualTypeOf<string>()
|
|
39
|
+
expectTypeOf(query3Data).toEqualTypeOf<string | undefined>()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('TData should be defined when passed through queryOptions', () => {
|
|
43
|
+
const options = queryOptions({
|
|
44
|
+
queryKey: ['key'],
|
|
45
|
+
queryFn: () => {
|
|
46
|
+
return {
|
|
47
|
+
wow: true,
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
initialData: {
|
|
51
|
+
wow: true,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
const queryResults = useQueries({ queries: [options] })
|
|
55
|
+
|
|
56
|
+
const data = queryResults[0].data
|
|
57
|
+
|
|
58
|
+
expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('it should be possible to define a different TData than TQueryFnData using select with queryOptions spread into useQuery', () => {
|
|
62
|
+
const query1 = queryOptions({
|
|
63
|
+
queryKey: ['key'],
|
|
64
|
+
queryFn: () => Promise.resolve(1),
|
|
65
|
+
select: (data) => data > 1,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const query2 = {
|
|
69
|
+
queryKey: ['key'],
|
|
70
|
+
queryFn: () => Promise.resolve(1),
|
|
71
|
+
select: (data: number) => data > 1,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const queryResults = useQueries({ queries: [query1, query2] })
|
|
75
|
+
const query1Data = queryResults[0].data
|
|
76
|
+
const query2Data = queryResults[1].data
|
|
77
|
+
|
|
78
|
+
expectTypeOf(query1Data).toEqualTypeOf<boolean | undefined>()
|
|
79
|
+
expectTypeOf(query2Data).toEqualTypeOf<boolean | undefined>()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => {
|
|
83
|
+
const queryResults = useQueries({
|
|
84
|
+
queries: [
|
|
85
|
+
{
|
|
86
|
+
queryKey: ['key'],
|
|
87
|
+
queryFn: () => {
|
|
88
|
+
return {
|
|
89
|
+
wow: true,
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
initialData: () => undefined as { wow: boolean } | undefined,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
const data = queryResults[0].data
|
|
98
|
+
|
|
99
|
+
expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('custom hook', () => {
|
|
103
|
+
it('should allow custom hooks using UseQueryOptions', () => {
|
|
104
|
+
type Data = string
|
|
105
|
+
|
|
106
|
+
const useCustomQueries = (
|
|
107
|
+
options?: Omit<UseQueryOptions<Data>, 'queryKey' | 'queryFn'>,
|
|
108
|
+
) => {
|
|
109
|
+
return useQueries({
|
|
110
|
+
queries: [
|
|
111
|
+
{
|
|
112
|
+
...options,
|
|
113
|
+
queryKey: ['todos-key'],
|
|
114
|
+
queryFn: () => Promise.resolve('data'),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const queryResults = useCustomQueries()
|
|
121
|
+
const data = queryResults[0].data
|
|
122
|
+
|
|
123
|
+
expectTypeOf(data).toEqualTypeOf<Data | undefined>()
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|