@tanstack/solid-query 5.0.0-alpha.51 → 5.0.0-alpha.53
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/lib/QueryClient.cjs +12 -0
- package/build/lib/QueryClient.cjs.map +1 -0
- package/build/lib/QueryClient.js +10 -0
- package/build/lib/QueryClient.js.map +1 -0
- package/build/lib/QueryClientProvider.cjs +35 -0
- package/build/lib/QueryClientProvider.cjs.map +1 -0
- package/build/lib/QueryClientProvider.js +31 -0
- package/build/lib/QueryClientProvider.js.map +1 -0
- package/build/lib/createBaseQuery.cjs +192 -0
- package/build/lib/createBaseQuery.cjs.map +1 -0
- package/build/lib/createBaseQuery.js +190 -0
- package/build/lib/createBaseQuery.js.map +1 -0
- package/build/lib/createInfiniteQuery.cjs +14 -0
- package/build/lib/createInfiniteQuery.cjs.map +1 -0
- package/build/lib/createInfiniteQuery.js +12 -0
- package/build/lib/createInfiniteQuery.js.map +1 -0
- package/build/lib/createMutation.cjs +44 -0
- package/build/lib/createMutation.cjs.map +1 -0
- package/build/lib/createMutation.js +42 -0
- package/build/lib/createMutation.js.map +1 -0
- package/build/lib/createQueries.cjs +52 -0
- package/build/lib/createQueries.cjs.map +1 -0
- package/build/lib/createQueries.js +50 -0
- package/build/lib/createQueries.js.map +1 -0
- package/build/lib/createQuery.cjs +16 -0
- package/build/lib/createQuery.cjs.map +1 -0
- package/build/lib/createQuery.js +13 -0
- package/build/lib/createQuery.js.map +1 -0
- package/build/lib/index.cjs.map +1 -0
- package/build/lib/index.js +11 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/setBatchUpdatesFn.cjs +7 -0
- package/build/lib/setBatchUpdatesFn.cjs.map +1 -0
- package/build/{source → lib}/setBatchUpdatesFn.js +2 -0
- package/build/lib/setBatchUpdatesFn.js.map +1 -0
- package/build/lib/useIsFetching.cjs +18 -0
- package/build/lib/useIsFetching.cjs.map +1 -0
- package/build/lib/useIsFetching.js +16 -0
- package/build/lib/useIsFetching.js.map +1 -0
- package/build/lib/useIsMutating.cjs +18 -0
- package/build/lib/useIsMutating.cjs.map +1 -0
- package/build/lib/useIsMutating.js +16 -0
- package/build/lib/useIsMutating.js.map +1 -0
- package/build/lib/utils.cjs +12 -0
- package/build/lib/utils.cjs.map +1 -0
- package/build/lib/utils.js +10 -0
- package/build/lib/utils.js.map +1 -0
- package/build/stats-html.html +6177 -0
- package/build/stats.json +605 -0
- package/package.json +6 -14
- package/build/source/QueryClient.js +0 -6
- package/build/source/QueryClientProvider.jsx +0 -21
- package/build/source/__tests__/QueryClientProvider.test.jsx +0 -120
- package/build/source/__tests__/createInfiniteQuery.test.jsx +0 -1360
- package/build/source/__tests__/createMutation.test.jsx +0 -867
- package/build/source/__tests__/createQueries.test.jsx +0 -590
- package/build/source/__tests__/createQuery.test.jsx +0 -4398
- package/build/source/__tests__/createQuery.types.test.jsx +0 -153
- package/build/source/__tests__/suspense.test.jsx +0 -659
- package/build/source/__tests__/transition.test.jsx +0 -42
- package/build/source/__tests__/useIsFetching.test.jsx +0 -190
- package/build/source/__tests__/useIsMutating.test.jsx +0 -196
- package/build/source/__tests__/utils.jsx +0 -50
- package/build/source/createBaseQuery.js +0 -173
- package/build/source/createInfiniteQuery.js +0 -8
- package/build/source/createMutation.js +0 -38
- package/build/source/createQueries.js +0 -38
- package/build/source/createQuery.js +0 -9
- package/build/source/index.js +0 -15
- package/build/source/types.js +0 -2
- package/build/source/useIsFetching.js +0 -12
- package/build/source/useIsMutating.js +0 -12
- package/build/source/utils.js +0 -7
- /package/build/{types → lib}/QueryClient.d.ts +0 -0
- /package/build/{types → lib}/QueryClient.d.ts.map +0 -0
- /package/build/{types → lib}/QueryClientProvider.d.ts +0 -0
- /package/build/{types → lib}/QueryClientProvider.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/QueryClientProvider.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/QueryClientProvider.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/createInfiniteQuery.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/createInfiniteQuery.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/createMutation.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/createMutation.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/createQueries.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/createQueries.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/createQuery.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/createQuery.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/createQuery.types.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/createQuery.types.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/suspense.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/suspense.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/transition.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/transition.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/useIsFetching.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/useIsFetching.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/useIsMutating.test.d.ts +0 -0
- /package/build/{types → lib}/__tests__/useIsMutating.test.d.ts.map +0 -0
- /package/build/{types → lib}/__tests__/utils.d.ts +0 -0
- /package/build/{types → lib}/__tests__/utils.d.ts.map +0 -0
- /package/build/{types → lib}/createBaseQuery.d.ts +0 -0
- /package/build/{types → lib}/createBaseQuery.d.ts.map +0 -0
- /package/build/{types → lib}/createInfiniteQuery.d.ts +0 -0
- /package/build/{types → lib}/createInfiniteQuery.d.ts.map +0 -0
- /package/build/{types → lib}/createMutation.d.ts +0 -0
- /package/build/{types → lib}/createMutation.d.ts.map +0 -0
- /package/build/{types → lib}/createQueries.d.ts +0 -0
- /package/build/{types → lib}/createQueries.d.ts.map +0 -0
- /package/build/{types → lib}/createQuery.d.ts +0 -0
- /package/build/{types → lib}/createQuery.d.ts.map +0 -0
- /package/build/{types → lib}/index.d.ts +0 -0
- /package/build/{types → lib}/index.d.ts.map +0 -0
- /package/build/{types → lib}/setBatchUpdatesFn.d.ts +0 -0
- /package/build/{types → lib}/setBatchUpdatesFn.d.ts.map +0 -0
- /package/build/{types → lib}/types.d.ts +0 -0
- /package/build/{types → lib}/types.d.ts.map +0 -0
- /package/build/{types → lib}/useIsFetching.d.ts +0 -0
- /package/build/{types → lib}/useIsFetching.d.ts.map +0 -0
- /package/build/{types → lib}/useIsMutating.d.ts +0 -0
- /package/build/{types → lib}/useIsMutating.d.ts.map +0 -0
- /package/build/{types → lib}/utils.d.ts +0 -0
- /package/build/{types → lib}/utils.d.ts.map +0 -0
|
@@ -1,4398 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|
|
2
|
-
import { createEffect, createMemo, createRenderEffect, createSignal, ErrorBoundary, Match, on, Switch, } from 'solid-js';
|
|
3
|
-
import { fireEvent, render, screen, waitFor } from '@solidjs/testing-library';
|
|
4
|
-
import { createQuery, QueryCache, QueryClientProvider, keepPreviousData, } from '..';
|
|
5
|
-
import { Blink, createQueryClient, expectType, mockNavigatorOnLine, mockVisibilityState, queryKey, setActTimeout, sleep, } from './utils';
|
|
6
|
-
import { vi } from 'vitest';
|
|
7
|
-
import { reconcile } from 'solid-js/store';
|
|
8
|
-
describe('createQuery', () => {
|
|
9
|
-
const queryCache = new QueryCache();
|
|
10
|
-
const queryClient = createQueryClient({ queryCache });
|
|
11
|
-
it('should return the correct types', () => {
|
|
12
|
-
const key = queryKey();
|
|
13
|
-
// @ts-ignore
|
|
14
|
-
// eslint-disable-next-line
|
|
15
|
-
function Page() {
|
|
16
|
-
// unspecified query function should default to unknown
|
|
17
|
-
const noQueryFn = createQuery(() => ({ queryKey: key }));
|
|
18
|
-
expectType(noQueryFn.data);
|
|
19
|
-
expectType(noQueryFn.error);
|
|
20
|
-
// it should infer the result type from the query function
|
|
21
|
-
const fromQueryFn = createQuery(() => ({
|
|
22
|
-
queryKey: key,
|
|
23
|
-
queryFn: () => 'test',
|
|
24
|
-
}));
|
|
25
|
-
expectType(fromQueryFn.data);
|
|
26
|
-
expectType(fromQueryFn.error);
|
|
27
|
-
// it should be possible to specify the result type
|
|
28
|
-
const withResult = createQuery(() => ({
|
|
29
|
-
queryKey: key,
|
|
30
|
-
queryFn: () => 'test',
|
|
31
|
-
}));
|
|
32
|
-
expectType(withResult.data);
|
|
33
|
-
expectType(withResult.error);
|
|
34
|
-
// it should be possible to specify the error type
|
|
35
|
-
const withError = createQuery(() => ({
|
|
36
|
-
queryKey: key,
|
|
37
|
-
queryFn: () => 'test',
|
|
38
|
-
}));
|
|
39
|
-
expectType(withError.data);
|
|
40
|
-
expectType(withError.error);
|
|
41
|
-
// it should provide the result type in the configuration
|
|
42
|
-
createQuery(() => ({
|
|
43
|
-
queryKey: [key],
|
|
44
|
-
queryFn: async () => true,
|
|
45
|
-
}));
|
|
46
|
-
// it should be possible to specify a union type as result type
|
|
47
|
-
const unionTypeSync = createQuery(() => ({
|
|
48
|
-
queryKey: key,
|
|
49
|
-
queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'),
|
|
50
|
-
}));
|
|
51
|
-
expectType(unionTypeSync.data);
|
|
52
|
-
const unionTypeAsync = createQuery(() => ({
|
|
53
|
-
queryKey: key,
|
|
54
|
-
queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'),
|
|
55
|
-
}));
|
|
56
|
-
expectType(unionTypeAsync.data);
|
|
57
|
-
// should error when the query function result does not match with the specified type
|
|
58
|
-
// @ts-expect-error
|
|
59
|
-
createQuery(() => ({ queryKey: key, queryFn: () => 'test' }));
|
|
60
|
-
// it should infer the result type from a generic query function
|
|
61
|
-
function queryFn() {
|
|
62
|
-
return Promise.resolve({});
|
|
63
|
-
}
|
|
64
|
-
const fromGenericQueryFn = createQuery(() => ({
|
|
65
|
-
queryKey: key,
|
|
66
|
-
queryFn: () => queryFn(),
|
|
67
|
-
}));
|
|
68
|
-
expectType(fromGenericQueryFn.data);
|
|
69
|
-
expectType(fromGenericQueryFn.error);
|
|
70
|
-
const fromGenericOptionsQueryFn = createQuery(() => ({
|
|
71
|
-
queryKey: key,
|
|
72
|
-
queryFn: () => queryFn(),
|
|
73
|
-
}));
|
|
74
|
-
expectType(fromGenericOptionsQueryFn.data);
|
|
75
|
-
expectType(fromGenericOptionsQueryFn.error);
|
|
76
|
-
const getMyDataArrayKey = async ({ queryKey: [, n], }) => {
|
|
77
|
-
return n + 42;
|
|
78
|
-
};
|
|
79
|
-
createQuery(() => ({
|
|
80
|
-
queryKey: ['my-data', 100],
|
|
81
|
-
queryFn: getMyDataArrayKey,
|
|
82
|
-
}));
|
|
83
|
-
const getMyDataStringKey = async (context) => {
|
|
84
|
-
expectType(context.queryKey);
|
|
85
|
-
return Number(context.queryKey[0]) + 42;
|
|
86
|
-
};
|
|
87
|
-
createQuery(() => ({
|
|
88
|
-
queryKey: ['1'],
|
|
89
|
-
queryFn: getMyDataStringKey,
|
|
90
|
-
}));
|
|
91
|
-
// it should handle query-functions that return Promise<any>
|
|
92
|
-
createQuery(() => ({
|
|
93
|
-
queryKey: key,
|
|
94
|
-
queryFn: () => fetch('return Promise<any>').then((resp) => resp.json()),
|
|
95
|
-
}));
|
|
96
|
-
// handles wrapped queries with custom fetcher passed as inline queryFn
|
|
97
|
-
const useWrappedQuery = (qk, fetcher, options) => createQuery(() => ({
|
|
98
|
-
queryKey: qk,
|
|
99
|
-
queryFn: () => fetcher(qk[1], 'token'),
|
|
100
|
-
...options,
|
|
101
|
-
}));
|
|
102
|
-
const test = useWrappedQuery([''], async () => '1');
|
|
103
|
-
expectType(test.data);
|
|
104
|
-
// handles wrapped queries with custom fetcher passed directly to createQuery
|
|
105
|
-
const useWrappedFuncStyleQuery = (qk, fetcher, options) => createQuery(() => ({ queryKey: qk, queryFn: fetcher, ...options }));
|
|
106
|
-
const testFuncStyle = useWrappedFuncStyleQuery([''], async () => true);
|
|
107
|
-
expectType(testFuncStyle.data);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
// See https://github.com/tannerlinsley/react-query/issues/105
|
|
111
|
-
it('should allow to set default data value', async () => {
|
|
112
|
-
const key = queryKey();
|
|
113
|
-
function Page() {
|
|
114
|
-
const state = createQuery(() => ({
|
|
115
|
-
queryKey: key,
|
|
116
|
-
queryFn: async () => {
|
|
117
|
-
await sleep(10);
|
|
118
|
-
return 'test';
|
|
119
|
-
},
|
|
120
|
-
}));
|
|
121
|
-
return (<div>
|
|
122
|
-
<h1>{state.data ?? 'default'}</h1>
|
|
123
|
-
</div>);
|
|
124
|
-
}
|
|
125
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
126
|
-
<Page />
|
|
127
|
-
</QueryClientProvider>));
|
|
128
|
-
screen.getByText('default');
|
|
129
|
-
await waitFor(() => screen.getByText('test'));
|
|
130
|
-
});
|
|
131
|
-
it('should return the correct states for a successful query', async () => {
|
|
132
|
-
const key = queryKey();
|
|
133
|
-
const states = [];
|
|
134
|
-
function Page() {
|
|
135
|
-
const state = createQuery(() => ({
|
|
136
|
-
queryKey: key,
|
|
137
|
-
queryFn: async () => {
|
|
138
|
-
await sleep(10);
|
|
139
|
-
return 'test';
|
|
140
|
-
},
|
|
141
|
-
}));
|
|
142
|
-
createRenderEffect(() => {
|
|
143
|
-
states.push({ ...state });
|
|
144
|
-
});
|
|
145
|
-
if (state.isPending) {
|
|
146
|
-
expectType(state.data);
|
|
147
|
-
expectType(state.error);
|
|
148
|
-
}
|
|
149
|
-
else if (state.isLoadingError) {
|
|
150
|
-
expectType(state.data);
|
|
151
|
-
expectType(state.error);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
expectType(state.data);
|
|
155
|
-
expectType(state.error);
|
|
156
|
-
}
|
|
157
|
-
return (<Switch fallback={<span>{state.data}</span>}>
|
|
158
|
-
<Match when={state.isPending}>
|
|
159
|
-
<span>pending</span>
|
|
160
|
-
</Match>
|
|
161
|
-
<Match when={state.isLoadingError}>
|
|
162
|
-
<span>{state.error.message}</span>
|
|
163
|
-
</Match>
|
|
164
|
-
</Switch>);
|
|
165
|
-
}
|
|
166
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
167
|
-
<Page />
|
|
168
|
-
</QueryClientProvider>));
|
|
169
|
-
await waitFor(() => screen.getByText('test'));
|
|
170
|
-
expect(states.length).toEqual(2);
|
|
171
|
-
expect(states[0]).toEqual({
|
|
172
|
-
data: undefined,
|
|
173
|
-
dataUpdatedAt: 0,
|
|
174
|
-
error: null,
|
|
175
|
-
errorUpdatedAt: 0,
|
|
176
|
-
failureCount: 0,
|
|
177
|
-
failureReason: null,
|
|
178
|
-
errorUpdateCount: 0,
|
|
179
|
-
isError: false,
|
|
180
|
-
isFetched: false,
|
|
181
|
-
isFetchedAfterMount: false,
|
|
182
|
-
isFetching: true,
|
|
183
|
-
isPaused: false,
|
|
184
|
-
isPending: true,
|
|
185
|
-
isInitialLoading: true,
|
|
186
|
-
isLoading: true,
|
|
187
|
-
isLoadingError: false,
|
|
188
|
-
isPlaceholderData: false,
|
|
189
|
-
isRefetchError: false,
|
|
190
|
-
isRefetching: false,
|
|
191
|
-
isStale: true,
|
|
192
|
-
isSuccess: false,
|
|
193
|
-
refetch: expect.any(Function),
|
|
194
|
-
status: 'pending',
|
|
195
|
-
fetchStatus: 'fetching',
|
|
196
|
-
});
|
|
197
|
-
expect(states[1]).toEqual({
|
|
198
|
-
data: 'test',
|
|
199
|
-
dataUpdatedAt: expect.any(Number),
|
|
200
|
-
error: null,
|
|
201
|
-
errorUpdatedAt: 0,
|
|
202
|
-
failureCount: 0,
|
|
203
|
-
failureReason: null,
|
|
204
|
-
errorUpdateCount: 0,
|
|
205
|
-
isError: false,
|
|
206
|
-
isFetched: true,
|
|
207
|
-
isFetchedAfterMount: true,
|
|
208
|
-
isFetching: false,
|
|
209
|
-
isPaused: false,
|
|
210
|
-
isPending: false,
|
|
211
|
-
isInitialLoading: false,
|
|
212
|
-
isLoading: false,
|
|
213
|
-
isLoadingError: false,
|
|
214
|
-
isPlaceholderData: false,
|
|
215
|
-
isRefetchError: false,
|
|
216
|
-
isRefetching: false,
|
|
217
|
-
isStale: true,
|
|
218
|
-
isSuccess: true,
|
|
219
|
-
refetch: expect.any(Function),
|
|
220
|
-
status: 'success',
|
|
221
|
-
fetchStatus: 'idle',
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
|
-
it('should return the correct states for an unsuccessful query', async () => {
|
|
225
|
-
const key = queryKey();
|
|
226
|
-
const states = [];
|
|
227
|
-
function Page() {
|
|
228
|
-
const state = createQuery(() => ({
|
|
229
|
-
queryKey: key,
|
|
230
|
-
queryFn: () => Promise.reject(new Error('rejected')),
|
|
231
|
-
retry: 1,
|
|
232
|
-
retryDelay: 1,
|
|
233
|
-
}));
|
|
234
|
-
createRenderEffect(() => {
|
|
235
|
-
states.push({ ...state });
|
|
236
|
-
});
|
|
237
|
-
return (<div>
|
|
238
|
-
<h1>Status: {state.status}</h1>
|
|
239
|
-
<div>Failure Count: {state.failureCount}</div>
|
|
240
|
-
<div>Failure Reason: {state.failureReason?.message}</div>
|
|
241
|
-
</div>);
|
|
242
|
-
}
|
|
243
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
244
|
-
<Page />
|
|
245
|
-
</QueryClientProvider>));
|
|
246
|
-
await waitFor(() => screen.getByText('Status: error'));
|
|
247
|
-
expect(states[0]).toEqual({
|
|
248
|
-
data: undefined,
|
|
249
|
-
dataUpdatedAt: 0,
|
|
250
|
-
error: null,
|
|
251
|
-
errorUpdatedAt: 0,
|
|
252
|
-
failureCount: 0,
|
|
253
|
-
failureReason: null,
|
|
254
|
-
errorUpdateCount: 0,
|
|
255
|
-
isError: false,
|
|
256
|
-
isFetched: false,
|
|
257
|
-
isFetchedAfterMount: false,
|
|
258
|
-
isFetching: true,
|
|
259
|
-
isPaused: false,
|
|
260
|
-
isPending: true,
|
|
261
|
-
isInitialLoading: true,
|
|
262
|
-
isLoading: true,
|
|
263
|
-
isLoadingError: false,
|
|
264
|
-
isPlaceholderData: false,
|
|
265
|
-
isRefetchError: false,
|
|
266
|
-
isRefetching: false,
|
|
267
|
-
isStale: true,
|
|
268
|
-
isSuccess: false,
|
|
269
|
-
refetch: expect.any(Function),
|
|
270
|
-
status: 'pending',
|
|
271
|
-
fetchStatus: 'fetching',
|
|
272
|
-
});
|
|
273
|
-
expect(states[1]).toEqual({
|
|
274
|
-
data: undefined,
|
|
275
|
-
dataUpdatedAt: 0,
|
|
276
|
-
error: null,
|
|
277
|
-
errorUpdatedAt: 0,
|
|
278
|
-
failureCount: 1,
|
|
279
|
-
failureReason: new Error('rejected'),
|
|
280
|
-
errorUpdateCount: 0,
|
|
281
|
-
isError: false,
|
|
282
|
-
isFetched: false,
|
|
283
|
-
isFetchedAfterMount: false,
|
|
284
|
-
isFetching: true,
|
|
285
|
-
isPaused: false,
|
|
286
|
-
isPending: true,
|
|
287
|
-
isInitialLoading: true,
|
|
288
|
-
isLoading: true,
|
|
289
|
-
isLoadingError: false,
|
|
290
|
-
isPlaceholderData: false,
|
|
291
|
-
isRefetchError: false,
|
|
292
|
-
isRefetching: false,
|
|
293
|
-
isStale: true,
|
|
294
|
-
isSuccess: false,
|
|
295
|
-
refetch: expect.any(Function),
|
|
296
|
-
status: 'pending',
|
|
297
|
-
fetchStatus: 'fetching',
|
|
298
|
-
});
|
|
299
|
-
expect(states[2]).toEqual({
|
|
300
|
-
data: undefined,
|
|
301
|
-
dataUpdatedAt: 0,
|
|
302
|
-
error: new Error('rejected'),
|
|
303
|
-
errorUpdatedAt: expect.any(Number),
|
|
304
|
-
failureCount: 2,
|
|
305
|
-
failureReason: new Error('rejected'),
|
|
306
|
-
errorUpdateCount: 1,
|
|
307
|
-
isError: true,
|
|
308
|
-
isFetched: true,
|
|
309
|
-
isFetchedAfterMount: true,
|
|
310
|
-
isFetching: false,
|
|
311
|
-
isPaused: false,
|
|
312
|
-
isPending: false,
|
|
313
|
-
isInitialLoading: false,
|
|
314
|
-
isLoading: false,
|
|
315
|
-
isLoadingError: true,
|
|
316
|
-
isPlaceholderData: false,
|
|
317
|
-
isRefetchError: false,
|
|
318
|
-
isRefetching: false,
|
|
319
|
-
isStale: true,
|
|
320
|
-
isSuccess: false,
|
|
321
|
-
refetch: expect.any(Function),
|
|
322
|
-
status: 'error',
|
|
323
|
-
fetchStatus: 'idle',
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
it('should set isFetchedAfterMount to true after a query has been fetched', async () => {
|
|
327
|
-
const key = queryKey();
|
|
328
|
-
const states = [];
|
|
329
|
-
await queryClient.prefetchQuery({
|
|
330
|
-
queryKey: key,
|
|
331
|
-
queryFn: () => 'prefetched',
|
|
332
|
-
});
|
|
333
|
-
function Page() {
|
|
334
|
-
const state = createQuery(() => ({
|
|
335
|
-
queryKey: key,
|
|
336
|
-
queryFn: () => 'data',
|
|
337
|
-
}));
|
|
338
|
-
createRenderEffect(() => {
|
|
339
|
-
states.push({ ...state });
|
|
340
|
-
});
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
344
|
-
<Page />
|
|
345
|
-
</QueryClientProvider>));
|
|
346
|
-
await sleep(10);
|
|
347
|
-
expect(states.length).toBe(2);
|
|
348
|
-
expect(states[0]).toMatchObject({
|
|
349
|
-
data: 'prefetched',
|
|
350
|
-
isFetched: true,
|
|
351
|
-
isFetchedAfterMount: false,
|
|
352
|
-
});
|
|
353
|
-
expect(states[1]).toMatchObject({
|
|
354
|
-
data: 'data',
|
|
355
|
-
isFetched: true,
|
|
356
|
-
isFetchedAfterMount: true,
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => {
|
|
360
|
-
const key = queryKey();
|
|
361
|
-
let fetchCount = 0;
|
|
362
|
-
function Page() {
|
|
363
|
-
const state = createQuery(() => ({
|
|
364
|
-
queryKey: key,
|
|
365
|
-
queryFn: async () => {
|
|
366
|
-
fetchCount++;
|
|
367
|
-
await sleep(10);
|
|
368
|
-
return 'data';
|
|
369
|
-
},
|
|
370
|
-
enabled: false,
|
|
371
|
-
initialData: 'initialData',
|
|
372
|
-
}));
|
|
373
|
-
createEffect(() => {
|
|
374
|
-
setActTimeout(() => {
|
|
375
|
-
state.refetch();
|
|
376
|
-
}, 5);
|
|
377
|
-
setActTimeout(() => {
|
|
378
|
-
state.refetch({ cancelRefetch: false });
|
|
379
|
-
}, 5);
|
|
380
|
-
});
|
|
381
|
-
return null;
|
|
382
|
-
}
|
|
383
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
384
|
-
<Page />
|
|
385
|
-
</QueryClientProvider>));
|
|
386
|
-
await sleep(20);
|
|
387
|
-
// first refetch only, second refetch is ignored
|
|
388
|
-
expect(fetchCount).toBe(1);
|
|
389
|
-
});
|
|
390
|
-
it('should cancel an ongoing fetch when refetch is called (cancelRefetch=true) if we have data already', async () => {
|
|
391
|
-
const key = queryKey();
|
|
392
|
-
let fetchCount = 0;
|
|
393
|
-
function Page() {
|
|
394
|
-
const state = createQuery(() => ({
|
|
395
|
-
queryKey: key,
|
|
396
|
-
queryFn: async () => {
|
|
397
|
-
fetchCount++;
|
|
398
|
-
await sleep(10);
|
|
399
|
-
return 'data';
|
|
400
|
-
},
|
|
401
|
-
enabled: false,
|
|
402
|
-
initialData: 'initialData',
|
|
403
|
-
}));
|
|
404
|
-
createEffect(() => {
|
|
405
|
-
setActTimeout(() => {
|
|
406
|
-
state.refetch();
|
|
407
|
-
}, 5);
|
|
408
|
-
setActTimeout(() => {
|
|
409
|
-
state.refetch();
|
|
410
|
-
}, 5);
|
|
411
|
-
});
|
|
412
|
-
return null;
|
|
413
|
-
}
|
|
414
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
415
|
-
<Page />
|
|
416
|
-
</QueryClientProvider>));
|
|
417
|
-
await sleep(20);
|
|
418
|
-
// first refetch (gets cancelled) and second refetch
|
|
419
|
-
expect(fetchCount).toBe(2);
|
|
420
|
-
});
|
|
421
|
-
it('should not cancel an ongoing fetch when refetch is called (cancelRefetch=true) if we do not have data yet', async () => {
|
|
422
|
-
const key = queryKey();
|
|
423
|
-
let fetchCount = 0;
|
|
424
|
-
function Page() {
|
|
425
|
-
const state = createQuery(() => ({
|
|
426
|
-
queryKey: key,
|
|
427
|
-
queryFn: async () => {
|
|
428
|
-
fetchCount++;
|
|
429
|
-
await sleep(10);
|
|
430
|
-
return 'data';
|
|
431
|
-
},
|
|
432
|
-
enabled: false,
|
|
433
|
-
}));
|
|
434
|
-
createEffect(() => {
|
|
435
|
-
setActTimeout(() => {
|
|
436
|
-
state.refetch();
|
|
437
|
-
}, 5);
|
|
438
|
-
setActTimeout(() => {
|
|
439
|
-
state.refetch();
|
|
440
|
-
}, 5);
|
|
441
|
-
});
|
|
442
|
-
return null;
|
|
443
|
-
}
|
|
444
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
445
|
-
<Page />
|
|
446
|
-
</QueryClientProvider>));
|
|
447
|
-
await sleep(20);
|
|
448
|
-
// first refetch will not get cancelled, second one gets skipped
|
|
449
|
-
expect(fetchCount).toBe(1);
|
|
450
|
-
});
|
|
451
|
-
it('should be able to watch a query without providing a query function', async () => {
|
|
452
|
-
const key = queryKey();
|
|
453
|
-
const states = [];
|
|
454
|
-
queryClient.setQueryDefaults(key, { queryFn: () => 'data' });
|
|
455
|
-
function Page() {
|
|
456
|
-
const state = createQuery(() => ({ queryKey: key }));
|
|
457
|
-
createRenderEffect(() => {
|
|
458
|
-
states.push({ ...state });
|
|
459
|
-
});
|
|
460
|
-
return null;
|
|
461
|
-
}
|
|
462
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
463
|
-
<Page />
|
|
464
|
-
</QueryClientProvider>));
|
|
465
|
-
await sleep(10);
|
|
466
|
-
expect(states.length).toBe(2);
|
|
467
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
468
|
-
expect(states[1]).toMatchObject({ data: 'data' });
|
|
469
|
-
});
|
|
470
|
-
it('should pick up a query when re-mounting with gcTime 0', async () => {
|
|
471
|
-
const key = queryKey();
|
|
472
|
-
const states = [];
|
|
473
|
-
function Page() {
|
|
474
|
-
const [toggle, setToggle] = createSignal(false);
|
|
475
|
-
return (<div>
|
|
476
|
-
<button onClick={() => setToggle(true)}>toggle</button>
|
|
477
|
-
<Switch>
|
|
478
|
-
<Match when={toggle()}>
|
|
479
|
-
<Component value="2"/>
|
|
480
|
-
</Match>
|
|
481
|
-
<Match when={!toggle()}>
|
|
482
|
-
<Component value="1"/>
|
|
483
|
-
</Match>
|
|
484
|
-
</Switch>
|
|
485
|
-
</div>);
|
|
486
|
-
}
|
|
487
|
-
function Component({ value }) {
|
|
488
|
-
const state = createQuery(() => ({
|
|
489
|
-
queryKey: key,
|
|
490
|
-
queryFn: async () => {
|
|
491
|
-
await sleep(10);
|
|
492
|
-
return 'data: ' + value;
|
|
493
|
-
},
|
|
494
|
-
gcTime: 0,
|
|
495
|
-
}));
|
|
496
|
-
createRenderEffect(() => {
|
|
497
|
-
states.push({ ...state });
|
|
498
|
-
});
|
|
499
|
-
return (<div>
|
|
500
|
-
<div>{state.data}</div>
|
|
501
|
-
</div>);
|
|
502
|
-
}
|
|
503
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
504
|
-
<Page />
|
|
505
|
-
</QueryClientProvider>));
|
|
506
|
-
await screen.findByText('data: 1');
|
|
507
|
-
fireEvent.click(screen.getByRole('button', { name: /toggle/i }));
|
|
508
|
-
await screen.findByText('data: 2');
|
|
509
|
-
expect(states.length).toBe(4);
|
|
510
|
-
// First load
|
|
511
|
-
expect(states[0]).toMatchObject({
|
|
512
|
-
isPending: true,
|
|
513
|
-
isSuccess: false,
|
|
514
|
-
isFetching: true,
|
|
515
|
-
});
|
|
516
|
-
// First success
|
|
517
|
-
expect(states[1]).toMatchObject({
|
|
518
|
-
isPending: false,
|
|
519
|
-
isSuccess: true,
|
|
520
|
-
isFetching: false,
|
|
521
|
-
});
|
|
522
|
-
// Switch, goes to fetching
|
|
523
|
-
expect(states[2]).toMatchObject({
|
|
524
|
-
isPending: false,
|
|
525
|
-
isSuccess: true,
|
|
526
|
-
isFetching: true,
|
|
527
|
-
});
|
|
528
|
-
// Second success
|
|
529
|
-
expect(states[3]).toMatchObject({
|
|
530
|
-
isPending: false,
|
|
531
|
-
isSuccess: true,
|
|
532
|
-
isFetching: false,
|
|
533
|
-
});
|
|
534
|
-
});
|
|
535
|
-
it('should fetch when refetchOnMount is false and nothing has been fetched yet', async () => {
|
|
536
|
-
const key = queryKey();
|
|
537
|
-
const states = [];
|
|
538
|
-
function Page() {
|
|
539
|
-
const state = createQuery(() => ({
|
|
540
|
-
queryKey: key,
|
|
541
|
-
queryFn: () => 'test',
|
|
542
|
-
refetchOnMount: false,
|
|
543
|
-
}));
|
|
544
|
-
createRenderEffect(() => {
|
|
545
|
-
states.push({ ...state });
|
|
546
|
-
});
|
|
547
|
-
return null;
|
|
548
|
-
}
|
|
549
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
550
|
-
<Page />
|
|
551
|
-
</QueryClientProvider>));
|
|
552
|
-
await sleep(10);
|
|
553
|
-
expect(states.length).toBe(2);
|
|
554
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
555
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
556
|
-
});
|
|
557
|
-
it('should not fetch when refetchOnMount is false and data has been fetched already', async () => {
|
|
558
|
-
const key = queryKey();
|
|
559
|
-
const states = [];
|
|
560
|
-
queryClient.setQueryData(key, 'prefetched');
|
|
561
|
-
function Page() {
|
|
562
|
-
const state = createQuery(() => ({
|
|
563
|
-
queryKey: key,
|
|
564
|
-
queryFn: () => 'test',
|
|
565
|
-
refetchOnMount: false,
|
|
566
|
-
}));
|
|
567
|
-
createRenderEffect(() => {
|
|
568
|
-
states.push({ ...state });
|
|
569
|
-
});
|
|
570
|
-
return null;
|
|
571
|
-
}
|
|
572
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
573
|
-
<Page />
|
|
574
|
-
</QueryClientProvider>));
|
|
575
|
-
await sleep(10);
|
|
576
|
-
expect(states.length).toBe(1);
|
|
577
|
-
expect(states[0]).toMatchObject({ data: 'prefetched' });
|
|
578
|
-
});
|
|
579
|
-
it('should be able to select a part of the data with select', async () => {
|
|
580
|
-
const key = queryKey();
|
|
581
|
-
const states = [];
|
|
582
|
-
function Page() {
|
|
583
|
-
const state = createQuery(() => ({
|
|
584
|
-
queryKey: key,
|
|
585
|
-
queryFn: () => ({ name: 'test' }),
|
|
586
|
-
select: (data) => data.name,
|
|
587
|
-
}));
|
|
588
|
-
createRenderEffect(() => {
|
|
589
|
-
states.push({ ...state });
|
|
590
|
-
});
|
|
591
|
-
return null;
|
|
592
|
-
}
|
|
593
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
594
|
-
<Page />
|
|
595
|
-
</QueryClientProvider>));
|
|
596
|
-
await sleep(10);
|
|
597
|
-
expect(states.length).toBe(2);
|
|
598
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
599
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
600
|
-
});
|
|
601
|
-
it('should be able to select a part of the data with select in object syntax', async () => {
|
|
602
|
-
const key = queryKey();
|
|
603
|
-
const states = [];
|
|
604
|
-
function Page() {
|
|
605
|
-
const state = createQuery(() => ({
|
|
606
|
-
queryKey: key,
|
|
607
|
-
queryFn: () => ({ name: 'test' }),
|
|
608
|
-
select: (data) => data.name,
|
|
609
|
-
}));
|
|
610
|
-
createRenderEffect(() => {
|
|
611
|
-
states.push({ ...state });
|
|
612
|
-
});
|
|
613
|
-
return null;
|
|
614
|
-
}
|
|
615
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
616
|
-
<Page />
|
|
617
|
-
</QueryClientProvider>));
|
|
618
|
-
await sleep(10);
|
|
619
|
-
expect(states.length).toBe(2);
|
|
620
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
621
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
622
|
-
});
|
|
623
|
-
it('should be able to select a part of the data with select in object syntax', async () => {
|
|
624
|
-
const key = queryKey();
|
|
625
|
-
const states = [];
|
|
626
|
-
function Page() {
|
|
627
|
-
const state = createQuery(() => ({
|
|
628
|
-
queryKey: key,
|
|
629
|
-
queryFn: () => ({ name: 'test' }),
|
|
630
|
-
select: (data) => data.name,
|
|
631
|
-
}));
|
|
632
|
-
createRenderEffect(() => {
|
|
633
|
-
states.push({ ...state });
|
|
634
|
-
});
|
|
635
|
-
return null;
|
|
636
|
-
}
|
|
637
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
638
|
-
<Page />
|
|
639
|
-
</QueryClientProvider>));
|
|
640
|
-
await sleep(10);
|
|
641
|
-
expect(states.length).toBe(2);
|
|
642
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
643
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
644
|
-
});
|
|
645
|
-
it('should not re-render when it should only re-render only data change and the selected data did not change', async () => {
|
|
646
|
-
const key = queryKey();
|
|
647
|
-
const states = [];
|
|
648
|
-
function Page() {
|
|
649
|
-
const state = createQuery(() => ({
|
|
650
|
-
queryKey: key,
|
|
651
|
-
queryFn: async () => {
|
|
652
|
-
await sleep(10);
|
|
653
|
-
return { name: 'test' };
|
|
654
|
-
},
|
|
655
|
-
select: (data) => data.name,
|
|
656
|
-
notifyOnChangeProps: ['data'],
|
|
657
|
-
}));
|
|
658
|
-
createRenderEffect(() => {
|
|
659
|
-
states.push({ ...state });
|
|
660
|
-
});
|
|
661
|
-
return (<div>
|
|
662
|
-
data: {state.data}
|
|
663
|
-
<button onClick={() => state.refetch()}>refetch</button>
|
|
664
|
-
</div>);
|
|
665
|
-
}
|
|
666
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
667
|
-
<Page />
|
|
668
|
-
</QueryClientProvider>));
|
|
669
|
-
await waitFor(() => screen.getByText('data: test'));
|
|
670
|
-
expect(states.length).toBe(2);
|
|
671
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
672
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
673
|
-
});
|
|
674
|
-
it('should throw an error when a selector throws', async () => {
|
|
675
|
-
const key = queryKey();
|
|
676
|
-
const states = [];
|
|
677
|
-
const error = new Error('Select Error');
|
|
678
|
-
function Page() {
|
|
679
|
-
const state = createQuery(() => ({
|
|
680
|
-
queryKey: key,
|
|
681
|
-
queryFn: () => ({ name: 'test' }),
|
|
682
|
-
select: () => {
|
|
683
|
-
throw error;
|
|
684
|
-
},
|
|
685
|
-
}));
|
|
686
|
-
createRenderEffect(() => {
|
|
687
|
-
states.push({ ...state });
|
|
688
|
-
});
|
|
689
|
-
return null;
|
|
690
|
-
}
|
|
691
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
692
|
-
<Page />
|
|
693
|
-
</QueryClientProvider>));
|
|
694
|
-
await sleep(10);
|
|
695
|
-
expect(states.length).toBe(2);
|
|
696
|
-
expect(states[0]).toMatchObject({ status: 'pending', data: undefined });
|
|
697
|
-
expect(states[1]).toMatchObject({ status: 'error', error });
|
|
698
|
-
});
|
|
699
|
-
it('should track properties and only re-render when a tracked property changes', async () => {
|
|
700
|
-
const key = queryKey();
|
|
701
|
-
const states = [];
|
|
702
|
-
function Page() {
|
|
703
|
-
const state = createQuery(() => ({
|
|
704
|
-
queryKey: key,
|
|
705
|
-
queryFn: async () => {
|
|
706
|
-
await sleep(10);
|
|
707
|
-
return 'test';
|
|
708
|
-
},
|
|
709
|
-
}));
|
|
710
|
-
createRenderEffect(() => {
|
|
711
|
-
states.push({ ...state });
|
|
712
|
-
});
|
|
713
|
-
createEffect(() => {
|
|
714
|
-
const data = state.data;
|
|
715
|
-
const refetch = state.refetch;
|
|
716
|
-
setActTimeout(() => {
|
|
717
|
-
if (data) {
|
|
718
|
-
refetch();
|
|
719
|
-
}
|
|
720
|
-
}, 20);
|
|
721
|
-
});
|
|
722
|
-
return (<div>
|
|
723
|
-
<h1>{state.data ?? null}</h1>
|
|
724
|
-
</div>);
|
|
725
|
-
}
|
|
726
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
727
|
-
<Page />
|
|
728
|
-
</QueryClientProvider>));
|
|
729
|
-
await waitFor(() => screen.getByText('test'));
|
|
730
|
-
expect(states.length).toBe(2);
|
|
731
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
732
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
733
|
-
});
|
|
734
|
-
it('should always re-render if we are tracking props but not using any', async () => {
|
|
735
|
-
const key = queryKey();
|
|
736
|
-
let renderCount = 0;
|
|
737
|
-
const states = [];
|
|
738
|
-
function Page() {
|
|
739
|
-
const state = createQuery(() => ({
|
|
740
|
-
queryKey: key,
|
|
741
|
-
queryFn: () => 'test',
|
|
742
|
-
}));
|
|
743
|
-
createRenderEffect(() => {
|
|
744
|
-
states.push({ ...state });
|
|
745
|
-
});
|
|
746
|
-
createEffect(on(() => ({ ...state }), () => {
|
|
747
|
-
renderCount++;
|
|
748
|
-
}));
|
|
749
|
-
return (<div>
|
|
750
|
-
<h1>hello</h1>
|
|
751
|
-
</div>);
|
|
752
|
-
}
|
|
753
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
754
|
-
<Page />
|
|
755
|
-
</QueryClientProvider>));
|
|
756
|
-
await sleep(10);
|
|
757
|
-
expect(renderCount).toBe(2);
|
|
758
|
-
expect(states.length).toBe(2);
|
|
759
|
-
expect(states[0]).toMatchObject({ data: undefined });
|
|
760
|
-
expect(states[1]).toMatchObject({ data: 'test' });
|
|
761
|
-
});
|
|
762
|
-
it('should share equal data structures between query results', async () => {
|
|
763
|
-
const key = queryKey();
|
|
764
|
-
const result1 = [
|
|
765
|
-
{ id: '1', done: false },
|
|
766
|
-
{ id: '2', done: false },
|
|
767
|
-
];
|
|
768
|
-
const result2 = [
|
|
769
|
-
{ id: '1', done: false },
|
|
770
|
-
{ id: '2', done: true },
|
|
771
|
-
];
|
|
772
|
-
const states = [];
|
|
773
|
-
let count = 0;
|
|
774
|
-
function Page() {
|
|
775
|
-
const state = createQuery(() => ({
|
|
776
|
-
queryKey: key,
|
|
777
|
-
queryFn: async () => {
|
|
778
|
-
await sleep(10);
|
|
779
|
-
count++;
|
|
780
|
-
return count === 1 ? result1 : result2;
|
|
781
|
-
},
|
|
782
|
-
}));
|
|
783
|
-
createRenderEffect(() => {
|
|
784
|
-
states.push({ ...state });
|
|
785
|
-
});
|
|
786
|
-
const { refetch } = state;
|
|
787
|
-
return (<div>
|
|
788
|
-
<button onClick={() => refetch()}>refetch</button>
|
|
789
|
-
data: {String(state.data?.[1]?.done)}
|
|
790
|
-
</div>);
|
|
791
|
-
}
|
|
792
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
793
|
-
<Page />
|
|
794
|
-
</QueryClientProvider>));
|
|
795
|
-
await waitFor(() => screen.getByText('data: false'));
|
|
796
|
-
await sleep(20);
|
|
797
|
-
fireEvent.click(screen.getByRole('button', { name: /refetch/i }));
|
|
798
|
-
await waitFor(() => screen.getByText('data: true'));
|
|
799
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
800
|
-
const todos = states[2]?.data;
|
|
801
|
-
const todo1 = todos?.[0];
|
|
802
|
-
const todo2 = todos?.[1];
|
|
803
|
-
const newTodos = states[3]?.data;
|
|
804
|
-
const newTodo1 = newTodos?.[0];
|
|
805
|
-
const newTodo2 = newTodos?.[1];
|
|
806
|
-
expect(todos).toEqual(result1);
|
|
807
|
-
expect(newTodos).toEqual(result2);
|
|
808
|
-
expect(newTodo1).toBe(todo1);
|
|
809
|
-
expect(newTodo2).toBe(todo2);
|
|
810
|
-
return null;
|
|
811
|
-
});
|
|
812
|
-
it('should use query function from hook when the existing query does not have a query function', async () => {
|
|
813
|
-
const key = queryKey();
|
|
814
|
-
const results = [];
|
|
815
|
-
queryClient.setQueryData(key, 'set');
|
|
816
|
-
function Page() {
|
|
817
|
-
const result = createQuery(() => ({
|
|
818
|
-
queryKey: key,
|
|
819
|
-
queryFn: async () => {
|
|
820
|
-
await sleep(10);
|
|
821
|
-
return 'fetched';
|
|
822
|
-
},
|
|
823
|
-
initialData: 'initial',
|
|
824
|
-
staleTime: Infinity,
|
|
825
|
-
}));
|
|
826
|
-
createRenderEffect(() => {
|
|
827
|
-
results.push({ ...result });
|
|
828
|
-
});
|
|
829
|
-
return (<div>
|
|
830
|
-
<div>isFetching: {result.isFetching}</div>
|
|
831
|
-
<button onClick={() => queryClient.refetchQueries({ queryKey: key })}>
|
|
832
|
-
refetch
|
|
833
|
-
</button>
|
|
834
|
-
data: {result.data}
|
|
835
|
-
</div>);
|
|
836
|
-
}
|
|
837
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
838
|
-
<Page />
|
|
839
|
-
</QueryClientProvider>));
|
|
840
|
-
await waitFor(() => screen.getByText('data: set'));
|
|
841
|
-
fireEvent.click(screen.getByRole('button', { name: /refetch/i }));
|
|
842
|
-
await waitFor(() => screen.getByText('data: fetched'));
|
|
843
|
-
await waitFor(() => expect(results.length).toBe(3));
|
|
844
|
-
expect(results[0]).toMatchObject({ data: 'set', isFetching: false });
|
|
845
|
-
expect(results[1]).toMatchObject({ data: 'set', isFetching: true });
|
|
846
|
-
expect(results[2]).toMatchObject({ data: 'fetched', isFetching: false });
|
|
847
|
-
});
|
|
848
|
-
it('should update query stale state and refetch when invalidated with invalidateQueries', async () => {
|
|
849
|
-
const key = queryKey();
|
|
850
|
-
const states = [];
|
|
851
|
-
let count = 0;
|
|
852
|
-
function Page() {
|
|
853
|
-
const state = createQuery(() => ({
|
|
854
|
-
queryKey: key,
|
|
855
|
-
queryFn: async () => {
|
|
856
|
-
await sleep(10);
|
|
857
|
-
count++;
|
|
858
|
-
return count;
|
|
859
|
-
},
|
|
860
|
-
staleTime: Infinity,
|
|
861
|
-
}));
|
|
862
|
-
createEffect(() => {
|
|
863
|
-
states.push({ ...state });
|
|
864
|
-
});
|
|
865
|
-
return (<div>
|
|
866
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
867
|
-
invalidate
|
|
868
|
-
</button>
|
|
869
|
-
data: {state.data}
|
|
870
|
-
</div>);
|
|
871
|
-
}
|
|
872
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
873
|
-
<Page />
|
|
874
|
-
</QueryClientProvider>));
|
|
875
|
-
await waitFor(() => screen.getByText('data: 1'));
|
|
876
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
877
|
-
await waitFor(() => screen.getByText('data: 2'));
|
|
878
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
879
|
-
expect(states[0]).toMatchObject({
|
|
880
|
-
data: undefined,
|
|
881
|
-
isFetching: true,
|
|
882
|
-
isRefetching: false,
|
|
883
|
-
isSuccess: false,
|
|
884
|
-
isStale: true,
|
|
885
|
-
});
|
|
886
|
-
expect(states[1]).toMatchObject({
|
|
887
|
-
data: 1,
|
|
888
|
-
isFetching: false,
|
|
889
|
-
isRefetching: false,
|
|
890
|
-
isSuccess: true,
|
|
891
|
-
isStale: false,
|
|
892
|
-
});
|
|
893
|
-
expect(states[2]).toMatchObject({
|
|
894
|
-
data: 1,
|
|
895
|
-
isFetching: true,
|
|
896
|
-
isRefetching: true,
|
|
897
|
-
isSuccess: true,
|
|
898
|
-
isStale: true,
|
|
899
|
-
});
|
|
900
|
-
expect(states[3]).toMatchObject({
|
|
901
|
-
data: 2,
|
|
902
|
-
isFetching: false,
|
|
903
|
-
isRefetching: false,
|
|
904
|
-
isSuccess: true,
|
|
905
|
-
isStale: false,
|
|
906
|
-
});
|
|
907
|
-
});
|
|
908
|
-
it('should not update disabled query when refetched with refetchQueries', async () => {
|
|
909
|
-
const key = queryKey();
|
|
910
|
-
const states = [];
|
|
911
|
-
let count = 0;
|
|
912
|
-
function Page() {
|
|
913
|
-
const state = createQuery(() => ({
|
|
914
|
-
queryKey: key,
|
|
915
|
-
queryFn: async () => {
|
|
916
|
-
await sleep(10);
|
|
917
|
-
count++;
|
|
918
|
-
return count;
|
|
919
|
-
},
|
|
920
|
-
enabled: false,
|
|
921
|
-
}));
|
|
922
|
-
createRenderEffect(() => {
|
|
923
|
-
states.push({ ...state });
|
|
924
|
-
});
|
|
925
|
-
createEffect(() => {
|
|
926
|
-
setActTimeout(() => {
|
|
927
|
-
queryClient.refetchQueries({ queryKey: key });
|
|
928
|
-
}, 20);
|
|
929
|
-
});
|
|
930
|
-
return null;
|
|
931
|
-
}
|
|
932
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
933
|
-
<Page />
|
|
934
|
-
</QueryClientProvider>));
|
|
935
|
-
await sleep(50);
|
|
936
|
-
expect(states.length).toBe(1);
|
|
937
|
-
expect(states[0]).toMatchObject({
|
|
938
|
-
data: undefined,
|
|
939
|
-
isFetching: false,
|
|
940
|
-
isSuccess: false,
|
|
941
|
-
isStale: true,
|
|
942
|
-
});
|
|
943
|
-
});
|
|
944
|
-
it('should not refetch disabled query when invalidated with invalidateQueries', async () => {
|
|
945
|
-
const key = queryKey();
|
|
946
|
-
const states = [];
|
|
947
|
-
let count = 0;
|
|
948
|
-
function Page() {
|
|
949
|
-
const state = createQuery(() => ({
|
|
950
|
-
queryKey: key,
|
|
951
|
-
queryFn: async () => {
|
|
952
|
-
await sleep(10);
|
|
953
|
-
count++;
|
|
954
|
-
return count;
|
|
955
|
-
},
|
|
956
|
-
enabled: false,
|
|
957
|
-
}));
|
|
958
|
-
createRenderEffect(() => {
|
|
959
|
-
states.push({ ...state });
|
|
960
|
-
});
|
|
961
|
-
createEffect(() => {
|
|
962
|
-
setActTimeout(() => {
|
|
963
|
-
queryClient.invalidateQueries({ queryKey: key });
|
|
964
|
-
}, 20);
|
|
965
|
-
});
|
|
966
|
-
return null;
|
|
967
|
-
}
|
|
968
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
969
|
-
<Page />
|
|
970
|
-
</QueryClientProvider>));
|
|
971
|
-
await sleep(100);
|
|
972
|
-
expect(states.length).toBe(1);
|
|
973
|
-
expect(states[0]).toMatchObject({
|
|
974
|
-
data: undefined,
|
|
975
|
-
isFetching: false,
|
|
976
|
-
isSuccess: false,
|
|
977
|
-
isStale: true,
|
|
978
|
-
});
|
|
979
|
-
});
|
|
980
|
-
it('should not fetch when switching to a disabled query', async () => {
|
|
981
|
-
const key = queryKey();
|
|
982
|
-
const states = [];
|
|
983
|
-
function Page() {
|
|
984
|
-
const [count, setCount] = createSignal(0);
|
|
985
|
-
const state = createQuery(() => ({
|
|
986
|
-
queryKey: [key, count()],
|
|
987
|
-
queryFn: async () => {
|
|
988
|
-
await sleep(5);
|
|
989
|
-
return count();
|
|
990
|
-
},
|
|
991
|
-
enabled: count() === 0,
|
|
992
|
-
}));
|
|
993
|
-
createRenderEffect(() => {
|
|
994
|
-
states.push({ ...state });
|
|
995
|
-
});
|
|
996
|
-
createEffect(() => {
|
|
997
|
-
setActTimeout(() => {
|
|
998
|
-
setCount(1);
|
|
999
|
-
}, 10);
|
|
1000
|
-
});
|
|
1001
|
-
return null;
|
|
1002
|
-
}
|
|
1003
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1004
|
-
<Page />
|
|
1005
|
-
</QueryClientProvider>));
|
|
1006
|
-
await sleep(50);
|
|
1007
|
-
expect(states.length).toBe(3);
|
|
1008
|
-
// Fetch query
|
|
1009
|
-
expect(states[0]).toMatchObject({
|
|
1010
|
-
isFetching: true,
|
|
1011
|
-
isSuccess: false,
|
|
1012
|
-
});
|
|
1013
|
-
// Fetched query
|
|
1014
|
-
expect(states[1]).toMatchObject({
|
|
1015
|
-
data: 0,
|
|
1016
|
-
isFetching: false,
|
|
1017
|
-
isSuccess: true,
|
|
1018
|
-
});
|
|
1019
|
-
// Switch to disabled query
|
|
1020
|
-
expect(states[2]).toMatchObject({
|
|
1021
|
-
isFetching: false,
|
|
1022
|
-
isSuccess: false,
|
|
1023
|
-
});
|
|
1024
|
-
});
|
|
1025
|
-
it('should keep the previous data when placeholderData is set', async () => {
|
|
1026
|
-
const key = queryKey();
|
|
1027
|
-
const states = [];
|
|
1028
|
-
function Page() {
|
|
1029
|
-
const [count, setCount] = createSignal(0);
|
|
1030
|
-
const state = createQuery(() => ({
|
|
1031
|
-
queryKey: [key, count()],
|
|
1032
|
-
queryFn: async () => {
|
|
1033
|
-
await sleep(10);
|
|
1034
|
-
return count();
|
|
1035
|
-
},
|
|
1036
|
-
placeholderData: keepPreviousData,
|
|
1037
|
-
}));
|
|
1038
|
-
createRenderEffect(() => {
|
|
1039
|
-
states.push({ ...state });
|
|
1040
|
-
});
|
|
1041
|
-
createEffect(() => {
|
|
1042
|
-
setActTimeout(() => {
|
|
1043
|
-
setCount(1);
|
|
1044
|
-
}, 20);
|
|
1045
|
-
});
|
|
1046
|
-
return null;
|
|
1047
|
-
}
|
|
1048
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1049
|
-
<Page />
|
|
1050
|
-
</QueryClientProvider>));
|
|
1051
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
1052
|
-
// Initial
|
|
1053
|
-
expect(states[0]).toMatchObject({
|
|
1054
|
-
data: undefined,
|
|
1055
|
-
isFetching: true,
|
|
1056
|
-
isSuccess: false,
|
|
1057
|
-
isPlaceholderData: false,
|
|
1058
|
-
});
|
|
1059
|
-
// Fetched
|
|
1060
|
-
expect(states[1]).toMatchObject({
|
|
1061
|
-
data: 0,
|
|
1062
|
-
isFetching: false,
|
|
1063
|
-
isSuccess: true,
|
|
1064
|
-
isPlaceholderData: false,
|
|
1065
|
-
});
|
|
1066
|
-
// Set state
|
|
1067
|
-
expect(states[2]).toMatchObject({
|
|
1068
|
-
data: 0,
|
|
1069
|
-
isFetching: true,
|
|
1070
|
-
isSuccess: true,
|
|
1071
|
-
isPlaceholderData: true,
|
|
1072
|
-
});
|
|
1073
|
-
// New data
|
|
1074
|
-
expect(states[3]).toMatchObject({
|
|
1075
|
-
data: 1,
|
|
1076
|
-
isFetching: false,
|
|
1077
|
-
isSuccess: true,
|
|
1078
|
-
isPlaceholderData: false,
|
|
1079
|
-
});
|
|
1080
|
-
});
|
|
1081
|
-
it('should not show initial data from next query if placeholderData is set', async () => {
|
|
1082
|
-
const key = queryKey();
|
|
1083
|
-
const states = [];
|
|
1084
|
-
function Page() {
|
|
1085
|
-
const [count, setCount] = createSignal(0);
|
|
1086
|
-
const state = createQuery(() => ({
|
|
1087
|
-
queryKey: [key, count()],
|
|
1088
|
-
queryFn: async () => {
|
|
1089
|
-
await sleep(10);
|
|
1090
|
-
return count();
|
|
1091
|
-
},
|
|
1092
|
-
initialData: 99,
|
|
1093
|
-
placeholderData: keepPreviousData,
|
|
1094
|
-
}));
|
|
1095
|
-
createRenderEffect(() => {
|
|
1096
|
-
states.push({ ...state });
|
|
1097
|
-
});
|
|
1098
|
-
return (<div>
|
|
1099
|
-
<h1>
|
|
1100
|
-
data: {state.data}, count: {count}, isFetching:{' '}
|
|
1101
|
-
{String(state.isFetching)}
|
|
1102
|
-
</h1>
|
|
1103
|
-
<button onClick={() => setCount(1)}>inc</button>
|
|
1104
|
-
</div>);
|
|
1105
|
-
}
|
|
1106
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1107
|
-
<Page />
|
|
1108
|
-
</QueryClientProvider>));
|
|
1109
|
-
await waitFor(() => screen.getByText('data: 0, count: 0, isFetching: false'));
|
|
1110
|
-
fireEvent.click(screen.getByRole('button', { name: 'inc' }));
|
|
1111
|
-
await waitFor(() => screen.getByText('data: 1, count: 1, isFetching: false'));
|
|
1112
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
1113
|
-
// Initial
|
|
1114
|
-
expect(states[0]).toMatchObject({
|
|
1115
|
-
data: 99,
|
|
1116
|
-
isFetching: true,
|
|
1117
|
-
isSuccess: true,
|
|
1118
|
-
isPlaceholderData: false,
|
|
1119
|
-
});
|
|
1120
|
-
// Fetched
|
|
1121
|
-
expect(states[1]).toMatchObject({
|
|
1122
|
-
data: 0,
|
|
1123
|
-
isFetching: false,
|
|
1124
|
-
isSuccess: true,
|
|
1125
|
-
isPlaceholderData: false,
|
|
1126
|
-
});
|
|
1127
|
-
// Set state
|
|
1128
|
-
expect(states[2]).toMatchObject({
|
|
1129
|
-
data: 99,
|
|
1130
|
-
isFetching: true,
|
|
1131
|
-
isSuccess: true,
|
|
1132
|
-
isPlaceholderData: false,
|
|
1133
|
-
});
|
|
1134
|
-
// New data
|
|
1135
|
-
expect(states[3]).toMatchObject({
|
|
1136
|
-
data: 1,
|
|
1137
|
-
isFetching: false,
|
|
1138
|
-
isSuccess: true,
|
|
1139
|
-
isPlaceholderData: false,
|
|
1140
|
-
});
|
|
1141
|
-
});
|
|
1142
|
-
it('should keep the previous data on disabled query when placeholderData is set to identity function', async () => {
|
|
1143
|
-
const key = queryKey();
|
|
1144
|
-
const states = [];
|
|
1145
|
-
function Page() {
|
|
1146
|
-
const [count, setCount] = createSignal(0);
|
|
1147
|
-
const state = createQuery(() => ({
|
|
1148
|
-
queryKey: [key, count()],
|
|
1149
|
-
queryFn: async () => {
|
|
1150
|
-
await sleep(10);
|
|
1151
|
-
return count();
|
|
1152
|
-
},
|
|
1153
|
-
enabled: false,
|
|
1154
|
-
placeholderData: keepPreviousData,
|
|
1155
|
-
notifyOnChangeProps: 'all',
|
|
1156
|
-
}));
|
|
1157
|
-
createRenderEffect(() => {
|
|
1158
|
-
states.push({ ...state });
|
|
1159
|
-
});
|
|
1160
|
-
createEffect(() => {
|
|
1161
|
-
const refetch = state.refetch;
|
|
1162
|
-
refetch();
|
|
1163
|
-
setActTimeout(() => {
|
|
1164
|
-
setCount(1);
|
|
1165
|
-
}, 20);
|
|
1166
|
-
setActTimeout(() => {
|
|
1167
|
-
refetch();
|
|
1168
|
-
}, 30);
|
|
1169
|
-
});
|
|
1170
|
-
return null;
|
|
1171
|
-
}
|
|
1172
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1173
|
-
<Page />
|
|
1174
|
-
</QueryClientProvider>));
|
|
1175
|
-
await sleep(100);
|
|
1176
|
-
expect(states.length).toBe(6);
|
|
1177
|
-
// Disabled query
|
|
1178
|
-
expect(states[0]).toMatchObject({
|
|
1179
|
-
data: undefined,
|
|
1180
|
-
isFetching: false,
|
|
1181
|
-
isSuccess: false,
|
|
1182
|
-
isPlaceholderData: false,
|
|
1183
|
-
});
|
|
1184
|
-
// Fetching query
|
|
1185
|
-
expect(states[1]).toMatchObject({
|
|
1186
|
-
data: undefined,
|
|
1187
|
-
isFetching: true,
|
|
1188
|
-
isSuccess: false,
|
|
1189
|
-
isPlaceholderData: false,
|
|
1190
|
-
});
|
|
1191
|
-
// Fetched query
|
|
1192
|
-
expect(states[2]).toMatchObject({
|
|
1193
|
-
data: 0,
|
|
1194
|
-
isFetching: false,
|
|
1195
|
-
isSuccess: true,
|
|
1196
|
-
isPlaceholderData: false,
|
|
1197
|
-
});
|
|
1198
|
-
// Set state
|
|
1199
|
-
expect(states[3]).toMatchObject({
|
|
1200
|
-
data: 0,
|
|
1201
|
-
isFetching: false,
|
|
1202
|
-
isSuccess: true,
|
|
1203
|
-
isPlaceholderData: true,
|
|
1204
|
-
});
|
|
1205
|
-
// Fetching new query
|
|
1206
|
-
expect(states[4]).toMatchObject({
|
|
1207
|
-
data: 0,
|
|
1208
|
-
isFetching: true,
|
|
1209
|
-
isSuccess: true,
|
|
1210
|
-
isPlaceholderData: true,
|
|
1211
|
-
});
|
|
1212
|
-
// Fetched new query
|
|
1213
|
-
expect(states[5]).toMatchObject({
|
|
1214
|
-
data: 1,
|
|
1215
|
-
isFetching: false,
|
|
1216
|
-
isSuccess: true,
|
|
1217
|
-
isPlaceholderData: false,
|
|
1218
|
-
});
|
|
1219
|
-
});
|
|
1220
|
-
it('should keep the previous data on disabled query when placeholderData is set and switching query key multiple times', async () => {
|
|
1221
|
-
const key = queryKey();
|
|
1222
|
-
const states = [];
|
|
1223
|
-
queryClient.setQueryData([key, 10], 10);
|
|
1224
|
-
await sleep(10);
|
|
1225
|
-
function Page() {
|
|
1226
|
-
const [count, setCount] = createSignal(10);
|
|
1227
|
-
const state = createQuery(() => ({
|
|
1228
|
-
queryKey: [key, count()],
|
|
1229
|
-
queryFn: async () => {
|
|
1230
|
-
await sleep(10);
|
|
1231
|
-
return count();
|
|
1232
|
-
},
|
|
1233
|
-
enabled: false,
|
|
1234
|
-
placeholderData: keepPreviousData,
|
|
1235
|
-
notifyOnChangeProps: 'all',
|
|
1236
|
-
}));
|
|
1237
|
-
createRenderEffect(() => {
|
|
1238
|
-
states.push({ ...state });
|
|
1239
|
-
});
|
|
1240
|
-
createEffect(() => {
|
|
1241
|
-
const refetch = state.refetch;
|
|
1242
|
-
setActTimeout(() => {
|
|
1243
|
-
setCount(11);
|
|
1244
|
-
}, 20);
|
|
1245
|
-
setActTimeout(() => {
|
|
1246
|
-
setCount(12);
|
|
1247
|
-
}, 30);
|
|
1248
|
-
setActTimeout(() => {
|
|
1249
|
-
refetch();
|
|
1250
|
-
}, 40);
|
|
1251
|
-
});
|
|
1252
|
-
return null;
|
|
1253
|
-
}
|
|
1254
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1255
|
-
<Page />
|
|
1256
|
-
</QueryClientProvider>));
|
|
1257
|
-
await sleep(100);
|
|
1258
|
-
expect(states.length).toBe(4);
|
|
1259
|
-
// Disabled query
|
|
1260
|
-
expect(states[0]).toMatchObject({
|
|
1261
|
-
data: 10,
|
|
1262
|
-
isFetching: false,
|
|
1263
|
-
isSuccess: true,
|
|
1264
|
-
isPlaceholderData: false,
|
|
1265
|
-
});
|
|
1266
|
-
// Set state
|
|
1267
|
-
expect(states[1]).toMatchObject({
|
|
1268
|
-
data: 10,
|
|
1269
|
-
isFetching: false,
|
|
1270
|
-
isSuccess: true,
|
|
1271
|
-
isPlaceholderData: true,
|
|
1272
|
-
});
|
|
1273
|
-
// Refetch
|
|
1274
|
-
expect(states[2]).toMatchObject({
|
|
1275
|
-
data: 10,
|
|
1276
|
-
isFetching: true,
|
|
1277
|
-
isSuccess: true,
|
|
1278
|
-
isPlaceholderData: true,
|
|
1279
|
-
});
|
|
1280
|
-
// Refetch done
|
|
1281
|
-
expect(states[3]).toMatchObject({
|
|
1282
|
-
data: 12,
|
|
1283
|
-
isFetching: false,
|
|
1284
|
-
isSuccess: true,
|
|
1285
|
-
isPlaceholderData: false,
|
|
1286
|
-
});
|
|
1287
|
-
});
|
|
1288
|
-
it('should use the correct query function when components use different configurations', async () => {
|
|
1289
|
-
const key = queryKey();
|
|
1290
|
-
const states = [];
|
|
1291
|
-
function FirstComponent() {
|
|
1292
|
-
const state = createQuery(() => ({
|
|
1293
|
-
queryKey: key,
|
|
1294
|
-
queryFn: async () => {
|
|
1295
|
-
await sleep(10);
|
|
1296
|
-
return 1;
|
|
1297
|
-
},
|
|
1298
|
-
}));
|
|
1299
|
-
createRenderEffect(() => {
|
|
1300
|
-
states.push({ ...state });
|
|
1301
|
-
});
|
|
1302
|
-
return (<div>
|
|
1303
|
-
<button onClick={() => state.refetch()}>refetch</button>
|
|
1304
|
-
data: {state.data}
|
|
1305
|
-
</div>);
|
|
1306
|
-
}
|
|
1307
|
-
function SecondComponent() {
|
|
1308
|
-
createQuery(() => ({
|
|
1309
|
-
queryKey: key,
|
|
1310
|
-
queryFn: () => 2,
|
|
1311
|
-
}));
|
|
1312
|
-
return null;
|
|
1313
|
-
}
|
|
1314
|
-
function Page() {
|
|
1315
|
-
return (<>
|
|
1316
|
-
<FirstComponent />
|
|
1317
|
-
<SecondComponent />
|
|
1318
|
-
</>);
|
|
1319
|
-
}
|
|
1320
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1321
|
-
<Page />
|
|
1322
|
-
</QueryClientProvider>));
|
|
1323
|
-
await waitFor(() => screen.getByText('data: 1'));
|
|
1324
|
-
fireEvent.click(screen.getByRole('button', { name: /refetch/i }));
|
|
1325
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
1326
|
-
expect(states[0]).toMatchObject({
|
|
1327
|
-
data: undefined,
|
|
1328
|
-
});
|
|
1329
|
-
expect(states[1]).toMatchObject({
|
|
1330
|
-
data: 1,
|
|
1331
|
-
});
|
|
1332
|
-
expect(states[2]).toMatchObject({
|
|
1333
|
-
data: 1,
|
|
1334
|
-
});
|
|
1335
|
-
// This state should be 1 instead of 2
|
|
1336
|
-
expect(states[3]).toMatchObject({
|
|
1337
|
-
data: 1,
|
|
1338
|
-
});
|
|
1339
|
-
});
|
|
1340
|
-
it('should be able to set different stale times for a query', async () => {
|
|
1341
|
-
const key = queryKey();
|
|
1342
|
-
const states1 = [];
|
|
1343
|
-
const states2 = [];
|
|
1344
|
-
await queryClient.prefetchQuery({
|
|
1345
|
-
queryKey: key,
|
|
1346
|
-
queryFn: async () => {
|
|
1347
|
-
await sleep(10);
|
|
1348
|
-
return 'prefetch';
|
|
1349
|
-
},
|
|
1350
|
-
});
|
|
1351
|
-
await sleep(20);
|
|
1352
|
-
function FirstComponent() {
|
|
1353
|
-
const state = createQuery(() => ({
|
|
1354
|
-
queryKey: key,
|
|
1355
|
-
queryFn: async () => {
|
|
1356
|
-
await sleep(10);
|
|
1357
|
-
return 'one';
|
|
1358
|
-
},
|
|
1359
|
-
staleTime: 100,
|
|
1360
|
-
}));
|
|
1361
|
-
createRenderEffect(() => {
|
|
1362
|
-
states1.push({ ...state });
|
|
1363
|
-
});
|
|
1364
|
-
return null;
|
|
1365
|
-
}
|
|
1366
|
-
function SecondComponent() {
|
|
1367
|
-
const state = createQuery(() => ({
|
|
1368
|
-
queryKey: key,
|
|
1369
|
-
queryFn: async () => {
|
|
1370
|
-
await sleep(10);
|
|
1371
|
-
return 'two';
|
|
1372
|
-
},
|
|
1373
|
-
staleTime: 10,
|
|
1374
|
-
}));
|
|
1375
|
-
createRenderEffect(() => {
|
|
1376
|
-
states2.push({ ...state });
|
|
1377
|
-
});
|
|
1378
|
-
return null;
|
|
1379
|
-
}
|
|
1380
|
-
function Page() {
|
|
1381
|
-
return (<>
|
|
1382
|
-
<FirstComponent />
|
|
1383
|
-
<SecondComponent />
|
|
1384
|
-
</>);
|
|
1385
|
-
}
|
|
1386
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1387
|
-
<Page />
|
|
1388
|
-
</QueryClientProvider>));
|
|
1389
|
-
await sleep(200);
|
|
1390
|
-
expect(states1.length).toBe(4);
|
|
1391
|
-
expect(states2.length).toBe(3);
|
|
1392
|
-
expect(states1).toMatchObject([
|
|
1393
|
-
// First render
|
|
1394
|
-
{
|
|
1395
|
-
data: 'prefetch',
|
|
1396
|
-
isStale: false,
|
|
1397
|
-
},
|
|
1398
|
-
// Second createQuery started fetching
|
|
1399
|
-
{
|
|
1400
|
-
data: 'prefetch',
|
|
1401
|
-
isStale: false,
|
|
1402
|
-
},
|
|
1403
|
-
// Second createQuery data came in
|
|
1404
|
-
{
|
|
1405
|
-
data: 'two',
|
|
1406
|
-
isStale: false,
|
|
1407
|
-
},
|
|
1408
|
-
// Data became stale after 100ms
|
|
1409
|
-
{
|
|
1410
|
-
data: 'two',
|
|
1411
|
-
isStale: true,
|
|
1412
|
-
},
|
|
1413
|
-
]);
|
|
1414
|
-
expect(states2).toMatchObject([
|
|
1415
|
-
// First render, data is stale and starts fetching
|
|
1416
|
-
{
|
|
1417
|
-
data: 'prefetch',
|
|
1418
|
-
isStale: true,
|
|
1419
|
-
},
|
|
1420
|
-
// Second createQuery data came in
|
|
1421
|
-
{
|
|
1422
|
-
data: 'two',
|
|
1423
|
-
isStale: false,
|
|
1424
|
-
},
|
|
1425
|
-
// Data became stale after 5ms
|
|
1426
|
-
{
|
|
1427
|
-
data: 'two',
|
|
1428
|
-
isStale: true,
|
|
1429
|
-
},
|
|
1430
|
-
]);
|
|
1431
|
-
});
|
|
1432
|
-
it('should re-render when a query becomes stale', async () => {
|
|
1433
|
-
const key = queryKey();
|
|
1434
|
-
const states = [];
|
|
1435
|
-
function Page() {
|
|
1436
|
-
const state = createQuery(() => ({
|
|
1437
|
-
queryKey: key,
|
|
1438
|
-
queryFn: () => 'test',
|
|
1439
|
-
staleTime: 50,
|
|
1440
|
-
}));
|
|
1441
|
-
createRenderEffect(() => {
|
|
1442
|
-
states.push({ ...state });
|
|
1443
|
-
});
|
|
1444
|
-
return null;
|
|
1445
|
-
}
|
|
1446
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1447
|
-
<Page />
|
|
1448
|
-
</QueryClientProvider>));
|
|
1449
|
-
await sleep(100);
|
|
1450
|
-
expect(states.length).toBe(3);
|
|
1451
|
-
expect(states[0]).toMatchObject({ isStale: true });
|
|
1452
|
-
expect(states[1]).toMatchObject({ isStale: false });
|
|
1453
|
-
expect(states[2]).toMatchObject({ isStale: true });
|
|
1454
|
-
});
|
|
1455
|
-
it('should not re-render when it should only re-render on data changes and the data did not change', async () => {
|
|
1456
|
-
const key = queryKey();
|
|
1457
|
-
const states = [];
|
|
1458
|
-
function Page() {
|
|
1459
|
-
const state = createQuery(() => ({
|
|
1460
|
-
queryKey: key,
|
|
1461
|
-
queryFn: async () => {
|
|
1462
|
-
await sleep(5);
|
|
1463
|
-
return 'test';
|
|
1464
|
-
},
|
|
1465
|
-
notifyOnChangeProps: ['data'],
|
|
1466
|
-
}));
|
|
1467
|
-
createRenderEffect(() => {
|
|
1468
|
-
states.push({ ...state });
|
|
1469
|
-
});
|
|
1470
|
-
createEffect(() => {
|
|
1471
|
-
const refetch = state.refetch;
|
|
1472
|
-
setActTimeout(() => {
|
|
1473
|
-
refetch();
|
|
1474
|
-
}, 10);
|
|
1475
|
-
});
|
|
1476
|
-
return null;
|
|
1477
|
-
}
|
|
1478
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1479
|
-
<Page />
|
|
1480
|
-
</QueryClientProvider>));
|
|
1481
|
-
await sleep(30);
|
|
1482
|
-
expect(states.length).toBe(2);
|
|
1483
|
-
expect(states[0]).toMatchObject({
|
|
1484
|
-
data: undefined,
|
|
1485
|
-
status: 'pending',
|
|
1486
|
-
isFetching: true,
|
|
1487
|
-
});
|
|
1488
|
-
expect(states[1]).toMatchObject({
|
|
1489
|
-
data: 'test',
|
|
1490
|
-
status: 'success',
|
|
1491
|
-
isFetching: false,
|
|
1492
|
-
});
|
|
1493
|
-
});
|
|
1494
|
-
// See https://github.com/tannerlinsley/react-query/issues/137
|
|
1495
|
-
it('should not override initial data in dependent queries', async () => {
|
|
1496
|
-
const key1 = queryKey();
|
|
1497
|
-
const key2 = queryKey();
|
|
1498
|
-
function Page() {
|
|
1499
|
-
const first = createQuery(() => ({
|
|
1500
|
-
queryKey: key1,
|
|
1501
|
-
queryFn: () => 'data',
|
|
1502
|
-
enabled: false,
|
|
1503
|
-
initialData: 'init',
|
|
1504
|
-
}));
|
|
1505
|
-
const second = createQuery(() => ({
|
|
1506
|
-
queryKey: key2,
|
|
1507
|
-
queryFn: () => 'data',
|
|
1508
|
-
enabled: false,
|
|
1509
|
-
initialData: 'init',
|
|
1510
|
-
}));
|
|
1511
|
-
return (<div>
|
|
1512
|
-
<h2>First Data: {first.data}</h2>
|
|
1513
|
-
<h2>Second Data: {second.data}</h2>
|
|
1514
|
-
<div>First Status: {first.status}</div>
|
|
1515
|
-
<div>Second Status: {second.status}</div>
|
|
1516
|
-
</div>);
|
|
1517
|
-
}
|
|
1518
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1519
|
-
<Page />
|
|
1520
|
-
</QueryClientProvider>));
|
|
1521
|
-
screen.getByText('First Data: init');
|
|
1522
|
-
screen.getByText('Second Data: init');
|
|
1523
|
-
screen.getByText('First Status: success');
|
|
1524
|
-
screen.getByText('Second Status: success');
|
|
1525
|
-
});
|
|
1526
|
-
it('should not override query configuration on render', async () => {
|
|
1527
|
-
const key = queryKey();
|
|
1528
|
-
const queryFn1 = async () => {
|
|
1529
|
-
await sleep(10);
|
|
1530
|
-
return 'data1';
|
|
1531
|
-
};
|
|
1532
|
-
const queryFn2 = async () => {
|
|
1533
|
-
await sleep(10);
|
|
1534
|
-
return 'data2';
|
|
1535
|
-
};
|
|
1536
|
-
function Page() {
|
|
1537
|
-
createQuery(() => ({ queryKey: key, queryFn: queryFn1 }));
|
|
1538
|
-
createQuery(() => ({ queryKey: key, queryFn: queryFn2 }));
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1541
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1542
|
-
<Page />
|
|
1543
|
-
</QueryClientProvider>));
|
|
1544
|
-
expect(queryCache.find({ queryKey: key }).options.queryFn).toBe(queryFn1);
|
|
1545
|
-
});
|
|
1546
|
-
it('should batch re-renders', async () => {
|
|
1547
|
-
const key = queryKey();
|
|
1548
|
-
let renders = 0;
|
|
1549
|
-
const queryFn = async () => {
|
|
1550
|
-
await sleep(15);
|
|
1551
|
-
return 'data';
|
|
1552
|
-
};
|
|
1553
|
-
function Page() {
|
|
1554
|
-
createQuery(() => ({ queryKey: key, queryFn }));
|
|
1555
|
-
createQuery(() => ({ queryKey: key, queryFn }));
|
|
1556
|
-
renders++;
|
|
1557
|
-
return null;
|
|
1558
|
-
}
|
|
1559
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1560
|
-
<Page />
|
|
1561
|
-
</QueryClientProvider>));
|
|
1562
|
-
await sleep(20);
|
|
1563
|
-
// Since components are rendered once
|
|
1564
|
-
// There wiil only be one pass
|
|
1565
|
-
expect(renders).toBe(1);
|
|
1566
|
-
});
|
|
1567
|
-
it('should render latest data even if react has discarded certain renders', async () => {
|
|
1568
|
-
const key = queryKey();
|
|
1569
|
-
function Page() {
|
|
1570
|
-
const [, setNewState] = createSignal('state');
|
|
1571
|
-
const state = createQuery(() => ({
|
|
1572
|
-
queryKey: key,
|
|
1573
|
-
queryFn: () => 'data',
|
|
1574
|
-
}));
|
|
1575
|
-
createEffect(() => {
|
|
1576
|
-
setActTimeout(() => {
|
|
1577
|
-
queryClient.setQueryData(key, 'new');
|
|
1578
|
-
// Update with same state to make react discard the next render
|
|
1579
|
-
setNewState('state');
|
|
1580
|
-
}, 10);
|
|
1581
|
-
});
|
|
1582
|
-
return <div>{state.data}</div>;
|
|
1583
|
-
}
|
|
1584
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1585
|
-
<Page />
|
|
1586
|
-
</QueryClientProvider>));
|
|
1587
|
-
await waitFor(() => screen.getByText('new'));
|
|
1588
|
-
});
|
|
1589
|
-
// See https://github.com/tannerlinsley/react-query/issues/170
|
|
1590
|
-
it('should start with status pending, fetchStatus idle if enabled is false', async () => {
|
|
1591
|
-
const key1 = queryKey();
|
|
1592
|
-
const key2 = queryKey();
|
|
1593
|
-
function Page() {
|
|
1594
|
-
const first = createQuery(() => ({
|
|
1595
|
-
queryKey: key1,
|
|
1596
|
-
queryFn: () => 'data',
|
|
1597
|
-
enabled: false,
|
|
1598
|
-
}));
|
|
1599
|
-
const second = createQuery(() => ({
|
|
1600
|
-
queryKey: key2,
|
|
1601
|
-
queryFn: () => 'data',
|
|
1602
|
-
}));
|
|
1603
|
-
return (<div>
|
|
1604
|
-
<div>
|
|
1605
|
-
First Status: {first.status}, {first.fetchStatus}
|
|
1606
|
-
</div>
|
|
1607
|
-
<div>
|
|
1608
|
-
Second Status: {second.status}, {second.fetchStatus}
|
|
1609
|
-
</div>
|
|
1610
|
-
</div>);
|
|
1611
|
-
}
|
|
1612
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1613
|
-
<Page />
|
|
1614
|
-
</QueryClientProvider>));
|
|
1615
|
-
// use "act" to wait for state update and prevent console warning
|
|
1616
|
-
screen.getByText('First Status: pending, idle');
|
|
1617
|
-
await waitFor(() => screen.getByText('Second Status: pending, fetching'));
|
|
1618
|
-
await waitFor(() => screen.getByText('Second Status: success, idle'));
|
|
1619
|
-
});
|
|
1620
|
-
// See https://github.com/tannerlinsley/react-query/issues/144
|
|
1621
|
-
it('should be in "pending" state by default', async () => {
|
|
1622
|
-
const key = queryKey();
|
|
1623
|
-
function Page() {
|
|
1624
|
-
const { status } = createQuery(() => ({
|
|
1625
|
-
queryKey: key,
|
|
1626
|
-
queryFn: async () => {
|
|
1627
|
-
await sleep(10);
|
|
1628
|
-
return 'test';
|
|
1629
|
-
},
|
|
1630
|
-
}));
|
|
1631
|
-
return <div>status: {status}</div>;
|
|
1632
|
-
}
|
|
1633
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1634
|
-
<Page />
|
|
1635
|
-
</QueryClientProvider>));
|
|
1636
|
-
screen.getByText('status: pending');
|
|
1637
|
-
});
|
|
1638
|
-
// See https://github.com/tannerlinsley/react-query/issues/147
|
|
1639
|
-
it('should not pass stringified variables to query function', async () => {
|
|
1640
|
-
const key = queryKey();
|
|
1641
|
-
const variables = { number: 5, boolean: false, object: {}, array: [] };
|
|
1642
|
-
const states = [];
|
|
1643
|
-
function Page() {
|
|
1644
|
-
const state = createQuery(() => ({
|
|
1645
|
-
queryKey: [key, variables],
|
|
1646
|
-
queryFn: async (ctx) => {
|
|
1647
|
-
await sleep(10);
|
|
1648
|
-
return ctx.queryKey;
|
|
1649
|
-
},
|
|
1650
|
-
}));
|
|
1651
|
-
createRenderEffect(() => {
|
|
1652
|
-
states.push({ ...state });
|
|
1653
|
-
});
|
|
1654
|
-
return null;
|
|
1655
|
-
}
|
|
1656
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1657
|
-
<Page />
|
|
1658
|
-
</QueryClientProvider>));
|
|
1659
|
-
await sleep(20);
|
|
1660
|
-
expect(states[1]?.data).toEqual([key, variables]);
|
|
1661
|
-
});
|
|
1662
|
-
it('should not refetch query on focus when `enabled` is set to `false`', async () => {
|
|
1663
|
-
const key = queryKey();
|
|
1664
|
-
const queryFn = vi.fn().mockReturnValue('data');
|
|
1665
|
-
function Page() {
|
|
1666
|
-
const { data = 'default' } = createQuery(() => ({
|
|
1667
|
-
queryKey: key,
|
|
1668
|
-
queryFn,
|
|
1669
|
-
enabled: false,
|
|
1670
|
-
}));
|
|
1671
|
-
return (<div>
|
|
1672
|
-
<h1>{data}</h1>
|
|
1673
|
-
</div>);
|
|
1674
|
-
}
|
|
1675
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1676
|
-
<Page />
|
|
1677
|
-
</QueryClientProvider>));
|
|
1678
|
-
await waitFor(() => screen.getByText('default'));
|
|
1679
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
1680
|
-
expect(queryFn).not.toHaveBeenCalled();
|
|
1681
|
-
});
|
|
1682
|
-
it('should not refetch stale query on focus when `refetchOnWindowFocus` is set to `false`', async () => {
|
|
1683
|
-
const key = queryKey();
|
|
1684
|
-
const states = [];
|
|
1685
|
-
let count = 0;
|
|
1686
|
-
function Page() {
|
|
1687
|
-
const state = createQuery(() => ({
|
|
1688
|
-
queryKey: key,
|
|
1689
|
-
queryFn: () => count++,
|
|
1690
|
-
staleTime: 0,
|
|
1691
|
-
refetchOnWindowFocus: false,
|
|
1692
|
-
}));
|
|
1693
|
-
createRenderEffect(() => {
|
|
1694
|
-
states.push({ ...state });
|
|
1695
|
-
});
|
|
1696
|
-
return null;
|
|
1697
|
-
}
|
|
1698
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1699
|
-
<Page />
|
|
1700
|
-
</QueryClientProvider>));
|
|
1701
|
-
await sleep(10);
|
|
1702
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
1703
|
-
await sleep(10);
|
|
1704
|
-
expect(states.length).toBe(2);
|
|
1705
|
-
expect(states[0]).toMatchObject({ data: undefined, isFetching: true });
|
|
1706
|
-
expect(states[1]).toMatchObject({ data: 0, isFetching: false });
|
|
1707
|
-
});
|
|
1708
|
-
it('should not refetch stale query on focus when `refetchOnWindowFocus` is set to a function that returns `false`', async () => {
|
|
1709
|
-
const key = queryKey();
|
|
1710
|
-
const states = [];
|
|
1711
|
-
let count = 0;
|
|
1712
|
-
function Page() {
|
|
1713
|
-
const state = createQuery(() => ({
|
|
1714
|
-
queryKey: key,
|
|
1715
|
-
queryFn: () => count++,
|
|
1716
|
-
staleTime: 0,
|
|
1717
|
-
refetchOnWindowFocus: () => false,
|
|
1718
|
-
}));
|
|
1719
|
-
createRenderEffect(() => {
|
|
1720
|
-
states.push({ ...state });
|
|
1721
|
-
});
|
|
1722
|
-
return null;
|
|
1723
|
-
}
|
|
1724
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1725
|
-
<Page />
|
|
1726
|
-
</QueryClientProvider>));
|
|
1727
|
-
await sleep(10);
|
|
1728
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
1729
|
-
await sleep(10);
|
|
1730
|
-
expect(states.length).toBe(2);
|
|
1731
|
-
expect(states[0]).toMatchObject({ data: undefined, isFetching: true });
|
|
1732
|
-
expect(states[1]).toMatchObject({ data: 0, isFetching: false });
|
|
1733
|
-
});
|
|
1734
|
-
it('should not refetch fresh query on focus when `refetchOnWindowFocus` is set to `true`', async () => {
|
|
1735
|
-
const key = queryKey();
|
|
1736
|
-
const states = [];
|
|
1737
|
-
let count = 0;
|
|
1738
|
-
function Page() {
|
|
1739
|
-
const state = createQuery(() => ({
|
|
1740
|
-
queryKey: key,
|
|
1741
|
-
queryFn: () => count++,
|
|
1742
|
-
staleTime: Infinity,
|
|
1743
|
-
refetchOnWindowFocus: true,
|
|
1744
|
-
}));
|
|
1745
|
-
createRenderEffect(() => {
|
|
1746
|
-
states.push({ ...state });
|
|
1747
|
-
});
|
|
1748
|
-
return null;
|
|
1749
|
-
}
|
|
1750
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1751
|
-
<Page />
|
|
1752
|
-
</QueryClientProvider>));
|
|
1753
|
-
await sleep(10);
|
|
1754
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
1755
|
-
await sleep(10);
|
|
1756
|
-
expect(states.length).toBe(2);
|
|
1757
|
-
expect(states[0]).toMatchObject({ data: undefined, isFetching: true });
|
|
1758
|
-
expect(states[1]).toMatchObject({ data: 0, isFetching: false });
|
|
1759
|
-
});
|
|
1760
|
-
it('should refetch fresh query on focus when `refetchOnWindowFocus` is set to `always`', async () => {
|
|
1761
|
-
const key = queryKey();
|
|
1762
|
-
const states = [];
|
|
1763
|
-
let count = 0;
|
|
1764
|
-
function Page() {
|
|
1765
|
-
const state = createQuery(() => ({
|
|
1766
|
-
queryKey: key,
|
|
1767
|
-
queryFn: async () => {
|
|
1768
|
-
await sleep(10);
|
|
1769
|
-
return count++;
|
|
1770
|
-
},
|
|
1771
|
-
staleTime: Infinity,
|
|
1772
|
-
refetchOnWindowFocus: 'always',
|
|
1773
|
-
}));
|
|
1774
|
-
createRenderEffect(() => {
|
|
1775
|
-
states.push({ ...state });
|
|
1776
|
-
});
|
|
1777
|
-
return null;
|
|
1778
|
-
}
|
|
1779
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1780
|
-
<Page />
|
|
1781
|
-
</QueryClientProvider>));
|
|
1782
|
-
await sleep(20);
|
|
1783
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
1784
|
-
await sleep(20);
|
|
1785
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
1786
|
-
expect(states[0]).toMatchObject({ data: undefined, isFetching: true });
|
|
1787
|
-
expect(states[1]).toMatchObject({ data: 0, isFetching: false });
|
|
1788
|
-
expect(states[2]).toMatchObject({ data: 0, isFetching: true });
|
|
1789
|
-
expect(states[3]).toMatchObject({ data: 1, isFetching: false });
|
|
1790
|
-
});
|
|
1791
|
-
it('should calculate focus behaviour for refetchOnWindowFocus depending on function', async () => {
|
|
1792
|
-
const key = queryKey();
|
|
1793
|
-
const states = [];
|
|
1794
|
-
let count = 0;
|
|
1795
|
-
function Page() {
|
|
1796
|
-
const state = createQuery(() => ({
|
|
1797
|
-
queryKey: key,
|
|
1798
|
-
queryFn: async () => {
|
|
1799
|
-
await sleep(10);
|
|
1800
|
-
return count++;
|
|
1801
|
-
},
|
|
1802
|
-
staleTime: 0,
|
|
1803
|
-
retry: 0,
|
|
1804
|
-
refetchOnWindowFocus: (query) => (query.state.data || 0) < 1,
|
|
1805
|
-
}));
|
|
1806
|
-
createRenderEffect(() => {
|
|
1807
|
-
states.push({ ...state });
|
|
1808
|
-
});
|
|
1809
|
-
return <div>data: {state.data}</div>;
|
|
1810
|
-
}
|
|
1811
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1812
|
-
<Page />
|
|
1813
|
-
</QueryClientProvider>));
|
|
1814
|
-
await screen.findByText('data: 0');
|
|
1815
|
-
expect(states.length).toBe(2);
|
|
1816
|
-
expect(states[0]).toMatchObject({ data: undefined, isFetching: true });
|
|
1817
|
-
expect(states[1]).toMatchObject({ data: 0, isFetching: false });
|
|
1818
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
1819
|
-
await screen.findByText('data: 1');
|
|
1820
|
-
// refetch should happen
|
|
1821
|
-
expect(states.length).toBe(4);
|
|
1822
|
-
expect(states[2]).toMatchObject({ data: 0, isFetching: true });
|
|
1823
|
-
expect(states[3]).toMatchObject({ data: 1, isFetching: false });
|
|
1824
|
-
await sleep(20);
|
|
1825
|
-
// no more refetch now
|
|
1826
|
-
expect(states.length).toBe(4);
|
|
1827
|
-
});
|
|
1828
|
-
it('should refetch fresh query when refetchOnMount is set to always', async () => {
|
|
1829
|
-
const key = queryKey();
|
|
1830
|
-
const states = [];
|
|
1831
|
-
await queryClient.prefetchQuery({
|
|
1832
|
-
queryKey: key,
|
|
1833
|
-
queryFn: () => 'prefetched',
|
|
1834
|
-
});
|
|
1835
|
-
function Page() {
|
|
1836
|
-
const state = createQuery(() => ({
|
|
1837
|
-
queryKey: key,
|
|
1838
|
-
queryFn: () => 'data',
|
|
1839
|
-
refetchOnMount: 'always',
|
|
1840
|
-
staleTime: Infinity,
|
|
1841
|
-
}));
|
|
1842
|
-
createRenderEffect(() => {
|
|
1843
|
-
states.push({ ...state });
|
|
1844
|
-
});
|
|
1845
|
-
return null;
|
|
1846
|
-
}
|
|
1847
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1848
|
-
<Page />
|
|
1849
|
-
</QueryClientProvider>));
|
|
1850
|
-
await sleep(10);
|
|
1851
|
-
expect(states.length).toBe(2);
|
|
1852
|
-
expect(states[0]).toMatchObject({
|
|
1853
|
-
data: 'prefetched',
|
|
1854
|
-
isStale: false,
|
|
1855
|
-
isFetching: true,
|
|
1856
|
-
});
|
|
1857
|
-
expect(states[1]).toMatchObject({
|
|
1858
|
-
data: 'data',
|
|
1859
|
-
isStale: false,
|
|
1860
|
-
isFetching: false,
|
|
1861
|
-
});
|
|
1862
|
-
});
|
|
1863
|
-
it('should refetch stale query when refetchOnMount is set to true', async () => {
|
|
1864
|
-
const key = queryKey();
|
|
1865
|
-
const states = [];
|
|
1866
|
-
await queryClient.prefetchQuery({
|
|
1867
|
-
queryKey: key,
|
|
1868
|
-
queryFn: () => 'prefetched',
|
|
1869
|
-
});
|
|
1870
|
-
await sleep(10);
|
|
1871
|
-
function Page() {
|
|
1872
|
-
const state = createQuery(() => ({
|
|
1873
|
-
queryKey: key,
|
|
1874
|
-
queryFn: () => 'data',
|
|
1875
|
-
refetchOnMount: true,
|
|
1876
|
-
staleTime: 0,
|
|
1877
|
-
}));
|
|
1878
|
-
createRenderEffect(() => {
|
|
1879
|
-
states.push({ ...state });
|
|
1880
|
-
});
|
|
1881
|
-
return null;
|
|
1882
|
-
}
|
|
1883
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1884
|
-
<Page />
|
|
1885
|
-
</QueryClientProvider>));
|
|
1886
|
-
await sleep(10);
|
|
1887
|
-
expect(states.length).toBe(2);
|
|
1888
|
-
expect(states[0]).toMatchObject({
|
|
1889
|
-
data: 'prefetched',
|
|
1890
|
-
isStale: true,
|
|
1891
|
-
isFetching: true,
|
|
1892
|
-
});
|
|
1893
|
-
expect(states[1]).toMatchObject({
|
|
1894
|
-
data: 'data',
|
|
1895
|
-
isStale: true,
|
|
1896
|
-
isFetching: false,
|
|
1897
|
-
});
|
|
1898
|
-
});
|
|
1899
|
-
it('should set status to error if queryFn throws', async () => {
|
|
1900
|
-
const key = queryKey();
|
|
1901
|
-
function Page() {
|
|
1902
|
-
const state = createQuery(() => ({
|
|
1903
|
-
queryKey: key,
|
|
1904
|
-
queryFn: () => {
|
|
1905
|
-
return Promise.reject(new Error('Error test jaylen'));
|
|
1906
|
-
},
|
|
1907
|
-
retry: false,
|
|
1908
|
-
}));
|
|
1909
|
-
return (<div>
|
|
1910
|
-
<h1>{state.status}</h1>
|
|
1911
|
-
<h2>{state.error?.message}</h2>
|
|
1912
|
-
</div>);
|
|
1913
|
-
}
|
|
1914
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1915
|
-
<Page />
|
|
1916
|
-
</QueryClientProvider>));
|
|
1917
|
-
await waitFor(() => screen.getByText('error'));
|
|
1918
|
-
await waitFor(() => screen.getByText('Error test jaylen'));
|
|
1919
|
-
});
|
|
1920
|
-
it('should throw error if queryFn throws and throwOnError is in use', async () => {
|
|
1921
|
-
const key = queryKey();
|
|
1922
|
-
function Page() {
|
|
1923
|
-
const state = createQuery(() => ({
|
|
1924
|
-
queryKey: key,
|
|
1925
|
-
queryFn: () => Promise.reject(new Error('Error test jaylen')),
|
|
1926
|
-
retry: false,
|
|
1927
|
-
throwOnError: true,
|
|
1928
|
-
}));
|
|
1929
|
-
return (<div>
|
|
1930
|
-
<h1>{state.status}</h1>
|
|
1931
|
-
<h2>{state.error?.message}</h2>
|
|
1932
|
-
</div>);
|
|
1933
|
-
}
|
|
1934
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1935
|
-
<ErrorBoundary fallback={() => <div>error boundary</div>}>
|
|
1936
|
-
<Page />
|
|
1937
|
-
</ErrorBoundary>
|
|
1938
|
-
</QueryClientProvider>));
|
|
1939
|
-
await waitFor(() => screen.getByText('error boundary'));
|
|
1940
|
-
});
|
|
1941
|
-
it('should update with data if we observe no properties and throwOnError', async () => {
|
|
1942
|
-
const key = queryKey();
|
|
1943
|
-
let result;
|
|
1944
|
-
function Page() {
|
|
1945
|
-
const query = createQuery(() => ({
|
|
1946
|
-
queryKey: key,
|
|
1947
|
-
queryFn: () => Promise.resolve('data'),
|
|
1948
|
-
throwOnError: true,
|
|
1949
|
-
}));
|
|
1950
|
-
createEffect(() => {
|
|
1951
|
-
result = query;
|
|
1952
|
-
});
|
|
1953
|
-
return null;
|
|
1954
|
-
}
|
|
1955
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1956
|
-
<Page />
|
|
1957
|
-
</QueryClientProvider>));
|
|
1958
|
-
await sleep(10);
|
|
1959
|
-
expect(result?.data).toBe('data');
|
|
1960
|
-
});
|
|
1961
|
-
it('should set status to error instead of throwing when error should not be thrown', async () => {
|
|
1962
|
-
const key = queryKey();
|
|
1963
|
-
function Page() {
|
|
1964
|
-
const state = createQuery(() => ({
|
|
1965
|
-
queryKey: key,
|
|
1966
|
-
queryFn: () => Promise.reject(new Error('Local Error')),
|
|
1967
|
-
retry: false,
|
|
1968
|
-
throwOnError: (err) => err.message !== 'Local Error',
|
|
1969
|
-
}));
|
|
1970
|
-
return (<div>
|
|
1971
|
-
<h1>{state.status}</h1>
|
|
1972
|
-
<h2>{state.error?.message}</h2>
|
|
1973
|
-
</div>);
|
|
1974
|
-
}
|
|
1975
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1976
|
-
<ErrorBoundary fallback={() => <div>error boundary</div>}>
|
|
1977
|
-
<Page />
|
|
1978
|
-
</ErrorBoundary>
|
|
1979
|
-
</QueryClientProvider>));
|
|
1980
|
-
await waitFor(() => screen.getByText('error'));
|
|
1981
|
-
await waitFor(() => screen.getByText('Local Error'));
|
|
1982
|
-
});
|
|
1983
|
-
it('should throw error instead of setting status when error should be thrown', async () => {
|
|
1984
|
-
const key = queryKey();
|
|
1985
|
-
function Page() {
|
|
1986
|
-
const state = createQuery(() => ({
|
|
1987
|
-
queryKey: key,
|
|
1988
|
-
queryFn: () => Promise.reject(new Error('Remote Error')),
|
|
1989
|
-
retry: false,
|
|
1990
|
-
throwOnError: (err) => err.message !== 'Local Error',
|
|
1991
|
-
}));
|
|
1992
|
-
return (<div>
|
|
1993
|
-
<h1>{state.status}</h1>
|
|
1994
|
-
<h2>{state.error?.message ?? ''}</h2>
|
|
1995
|
-
</div>);
|
|
1996
|
-
}
|
|
1997
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1998
|
-
<ErrorBoundary fallback={(error) => (<div>
|
|
1999
|
-
<div>error boundary</div>
|
|
2000
|
-
{/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition */}
|
|
2001
|
-
<div>{error?.message}</div>
|
|
2002
|
-
</div>)}>
|
|
2003
|
-
<Page />
|
|
2004
|
-
</ErrorBoundary>
|
|
2005
|
-
</QueryClientProvider>));
|
|
2006
|
-
await waitFor(() => screen.getByText('error boundary'));
|
|
2007
|
-
await waitFor(() => screen.getByText('Remote Error'));
|
|
2008
|
-
});
|
|
2009
|
-
it('should continue retries when observers unmount and remount while waiting for a retry (#3031)', async () => {
|
|
2010
|
-
const key = queryKey();
|
|
2011
|
-
let count = 0;
|
|
2012
|
-
function Page() {
|
|
2013
|
-
const result = createQuery(() => ({
|
|
2014
|
-
queryKey: key,
|
|
2015
|
-
queryFn: async () => {
|
|
2016
|
-
count++;
|
|
2017
|
-
await sleep(10);
|
|
2018
|
-
return Promise.reject(new Error('some error'));
|
|
2019
|
-
},
|
|
2020
|
-
retry: 2,
|
|
2021
|
-
retryDelay: 100,
|
|
2022
|
-
}));
|
|
2023
|
-
return (<div>
|
|
2024
|
-
<div>error: {result.error?.message ?? 'null'}</div>
|
|
2025
|
-
<div>failureCount: {result.failureCount}</div>
|
|
2026
|
-
<div>failureReason: {result.failureReason?.message}</div>
|
|
2027
|
-
</div>);
|
|
2028
|
-
}
|
|
2029
|
-
function App() {
|
|
2030
|
-
const [show, setShow] = createSignal(true);
|
|
2031
|
-
const toggle = () => setShow((s) => !s);
|
|
2032
|
-
return (<div>
|
|
2033
|
-
<button onClick={toggle}>{show() ? 'hide' : 'show'}</button>
|
|
2034
|
-
{show() && <Page />}
|
|
2035
|
-
</div>);
|
|
2036
|
-
}
|
|
2037
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2038
|
-
<App />
|
|
2039
|
-
</QueryClientProvider>));
|
|
2040
|
-
await waitFor(() => screen.getByText('failureCount: 1'));
|
|
2041
|
-
await waitFor(() => screen.getByText('failureReason: some error'));
|
|
2042
|
-
fireEvent.click(screen.getByRole('button', { name: /hide/i }));
|
|
2043
|
-
await waitFor(() => screen.getByRole('button', { name: /show/i }));
|
|
2044
|
-
fireEvent.click(screen.getByRole('button', { name: /show/i }));
|
|
2045
|
-
await waitFor(() => screen.getByText('error: some error'));
|
|
2046
|
-
expect(count).toBe(3);
|
|
2047
|
-
});
|
|
2048
|
-
it('should restart when observers unmount and remount while waiting for a retry when query was cancelled in between (#3031)', async () => {
|
|
2049
|
-
const key = queryKey();
|
|
2050
|
-
let count = 0;
|
|
2051
|
-
function Page() {
|
|
2052
|
-
const result = createQuery(() => ({
|
|
2053
|
-
queryKey: key,
|
|
2054
|
-
queryFn: async () => {
|
|
2055
|
-
count++;
|
|
2056
|
-
await sleep(10);
|
|
2057
|
-
return Promise.reject(new Error('some error'));
|
|
2058
|
-
},
|
|
2059
|
-
retry: 2,
|
|
2060
|
-
retryDelay: 100,
|
|
2061
|
-
}));
|
|
2062
|
-
return (<div>
|
|
2063
|
-
<div>error: {result.error?.message ?? 'null'}</div>
|
|
2064
|
-
<div>failureCount: {result.failureCount}</div>
|
|
2065
|
-
<div>failureReason: {result.failureReason?.message}</div>
|
|
2066
|
-
</div>);
|
|
2067
|
-
}
|
|
2068
|
-
function App() {
|
|
2069
|
-
const [show, setShow] = createSignal(true);
|
|
2070
|
-
const toggle = () => setShow((s) => !s);
|
|
2071
|
-
return (<div>
|
|
2072
|
-
<button onClick={toggle}>{show() ? 'hide' : 'show'}</button>
|
|
2073
|
-
<button onClick={() => queryClient.cancelQueries({ queryKey: key })}>
|
|
2074
|
-
cancel
|
|
2075
|
-
</button>
|
|
2076
|
-
{show() && <Page />}
|
|
2077
|
-
</div>);
|
|
2078
|
-
}
|
|
2079
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2080
|
-
<App />
|
|
2081
|
-
</QueryClientProvider>));
|
|
2082
|
-
await waitFor(() => screen.getByText('failureCount: 1'));
|
|
2083
|
-
await waitFor(() => screen.getByText('failureReason: some error'));
|
|
2084
|
-
fireEvent.click(screen.getByRole('button', { name: /hide/i }));
|
|
2085
|
-
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
|
|
2086
|
-
await waitFor(() => screen.getByRole('button', { name: /show/i }));
|
|
2087
|
-
fireEvent.click(screen.getByRole('button', { name: /show/i }));
|
|
2088
|
-
await waitFor(() => screen.getByText('error: some error'));
|
|
2089
|
-
// initial fetch (1), which will be cancelled, followed by new mount(2) + 2 retries = 4
|
|
2090
|
-
expect(count).toBe(4);
|
|
2091
|
-
});
|
|
2092
|
-
it('should always fetch if refetchOnMount is set to always', async () => {
|
|
2093
|
-
const key = queryKey();
|
|
2094
|
-
const states = [];
|
|
2095
|
-
await queryClient.prefetchQuery({
|
|
2096
|
-
queryKey: key,
|
|
2097
|
-
queryFn: () => 'prefetched',
|
|
2098
|
-
});
|
|
2099
|
-
function Page() {
|
|
2100
|
-
const state = createQuery(() => ({
|
|
2101
|
-
queryKey: key,
|
|
2102
|
-
queryFn: () => 'data',
|
|
2103
|
-
refetchOnMount: 'always',
|
|
2104
|
-
staleTime: 50,
|
|
2105
|
-
}));
|
|
2106
|
-
createRenderEffect(() => {
|
|
2107
|
-
states.push({ ...state });
|
|
2108
|
-
});
|
|
2109
|
-
return (<div>
|
|
2110
|
-
<div>data: {state.data ?? 'null'}</div>
|
|
2111
|
-
<div>isFetching: {state.isFetching}</div>
|
|
2112
|
-
<div>isStale: {state.isStale}</div>
|
|
2113
|
-
</div>);
|
|
2114
|
-
}
|
|
2115
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2116
|
-
<Page />
|
|
2117
|
-
</QueryClientProvider>));
|
|
2118
|
-
await waitFor(() => screen.getByText('data: data'));
|
|
2119
|
-
await waitFor(() => expect(states.length).toBe(3));
|
|
2120
|
-
expect(states[0]).toMatchObject({
|
|
2121
|
-
data: 'prefetched',
|
|
2122
|
-
isStale: false,
|
|
2123
|
-
isFetching: true,
|
|
2124
|
-
});
|
|
2125
|
-
expect(states[1]).toMatchObject({
|
|
2126
|
-
data: 'data',
|
|
2127
|
-
isStale: false,
|
|
2128
|
-
isFetching: false,
|
|
2129
|
-
});
|
|
2130
|
-
expect(states[2]).toMatchObject({
|
|
2131
|
-
data: 'data',
|
|
2132
|
-
isStale: true,
|
|
2133
|
-
isFetching: false,
|
|
2134
|
-
});
|
|
2135
|
-
});
|
|
2136
|
-
it('should fetch if initial data is set', async () => {
|
|
2137
|
-
const key = queryKey();
|
|
2138
|
-
const states = [];
|
|
2139
|
-
function Page() {
|
|
2140
|
-
const state = createQuery(() => ({
|
|
2141
|
-
queryKey: key,
|
|
2142
|
-
queryFn: () => 'data',
|
|
2143
|
-
initialData: 'initial',
|
|
2144
|
-
}));
|
|
2145
|
-
createRenderEffect(() => {
|
|
2146
|
-
states.push({ ...state });
|
|
2147
|
-
});
|
|
2148
|
-
return null;
|
|
2149
|
-
}
|
|
2150
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2151
|
-
<Page />
|
|
2152
|
-
</QueryClientProvider>));
|
|
2153
|
-
await sleep(50);
|
|
2154
|
-
expect(states.length).toBe(2);
|
|
2155
|
-
expect(states[0]).toMatchObject({
|
|
2156
|
-
data: 'initial',
|
|
2157
|
-
isStale: true,
|
|
2158
|
-
isFetching: true,
|
|
2159
|
-
});
|
|
2160
|
-
expect(states[1]).toMatchObject({
|
|
2161
|
-
data: 'data',
|
|
2162
|
-
isStale: true,
|
|
2163
|
-
isFetching: false,
|
|
2164
|
-
});
|
|
2165
|
-
});
|
|
2166
|
-
it('should not fetch if initial data is set with a stale time', async () => {
|
|
2167
|
-
const key = queryKey();
|
|
2168
|
-
const states = [];
|
|
2169
|
-
function Page() {
|
|
2170
|
-
const state = createQuery(() => ({
|
|
2171
|
-
queryKey: key,
|
|
2172
|
-
queryFn: () => 'data',
|
|
2173
|
-
staleTime: 50,
|
|
2174
|
-
initialData: 'initial',
|
|
2175
|
-
}));
|
|
2176
|
-
createRenderEffect(() => {
|
|
2177
|
-
states.push({ ...state });
|
|
2178
|
-
});
|
|
2179
|
-
return null;
|
|
2180
|
-
}
|
|
2181
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2182
|
-
<Page />
|
|
2183
|
-
</QueryClientProvider>));
|
|
2184
|
-
await sleep(100);
|
|
2185
|
-
expect(states.length).toBe(2);
|
|
2186
|
-
expect(states[0]).toMatchObject({
|
|
2187
|
-
data: 'initial',
|
|
2188
|
-
isStale: false,
|
|
2189
|
-
isFetching: false,
|
|
2190
|
-
});
|
|
2191
|
-
expect(states[1]).toMatchObject({
|
|
2192
|
-
data: 'initial',
|
|
2193
|
-
isStale: true,
|
|
2194
|
-
isFetching: false,
|
|
2195
|
-
});
|
|
2196
|
-
});
|
|
2197
|
-
it('should fetch if initial data updated at is older than stale time', async () => {
|
|
2198
|
-
const key = queryKey();
|
|
2199
|
-
const states = [];
|
|
2200
|
-
const oneSecondAgo = Date.now() - 1000;
|
|
2201
|
-
function Page() {
|
|
2202
|
-
const state = createQuery(() => ({
|
|
2203
|
-
queryKey: key,
|
|
2204
|
-
queryFn: () => 'data',
|
|
2205
|
-
staleTime: 50,
|
|
2206
|
-
initialData: 'initial',
|
|
2207
|
-
initialDataUpdatedAt: oneSecondAgo,
|
|
2208
|
-
}));
|
|
2209
|
-
createRenderEffect(() => {
|
|
2210
|
-
states.push({ ...state });
|
|
2211
|
-
});
|
|
2212
|
-
return null;
|
|
2213
|
-
}
|
|
2214
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2215
|
-
<Page />
|
|
2216
|
-
</QueryClientProvider>));
|
|
2217
|
-
await sleep(100);
|
|
2218
|
-
expect(states.length).toBe(3);
|
|
2219
|
-
expect(states[0]).toMatchObject({
|
|
2220
|
-
data: 'initial',
|
|
2221
|
-
isStale: true,
|
|
2222
|
-
isFetching: true,
|
|
2223
|
-
});
|
|
2224
|
-
expect(states[1]).toMatchObject({
|
|
2225
|
-
data: 'data',
|
|
2226
|
-
isStale: false,
|
|
2227
|
-
isFetching: false,
|
|
2228
|
-
});
|
|
2229
|
-
expect(states[2]).toMatchObject({
|
|
2230
|
-
data: 'data',
|
|
2231
|
-
isStale: true,
|
|
2232
|
-
isFetching: false,
|
|
2233
|
-
});
|
|
2234
|
-
});
|
|
2235
|
-
it('should fetch if "initial data updated at" is exactly 0', async () => {
|
|
2236
|
-
const key = queryKey();
|
|
2237
|
-
const states = [];
|
|
2238
|
-
function Page() {
|
|
2239
|
-
const state = createQuery(() => ({
|
|
2240
|
-
queryKey: key,
|
|
2241
|
-
queryFn: () => 'data',
|
|
2242
|
-
staleTime: 10 * 1000,
|
|
2243
|
-
initialData: 'initial',
|
|
2244
|
-
initialDataUpdatedAt: 0,
|
|
2245
|
-
}));
|
|
2246
|
-
createRenderEffect(() => {
|
|
2247
|
-
states.push({ ...state });
|
|
2248
|
-
});
|
|
2249
|
-
return null;
|
|
2250
|
-
}
|
|
2251
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2252
|
-
<Page />
|
|
2253
|
-
</QueryClientProvider>));
|
|
2254
|
-
await sleep(100);
|
|
2255
|
-
expect(states.length).toBe(2);
|
|
2256
|
-
expect(states[0]).toMatchObject({
|
|
2257
|
-
data: 'initial',
|
|
2258
|
-
isStale: true,
|
|
2259
|
-
isFetching: true,
|
|
2260
|
-
});
|
|
2261
|
-
expect(states[1]).toMatchObject({
|
|
2262
|
-
data: 'data',
|
|
2263
|
-
isStale: false,
|
|
2264
|
-
isFetching: false,
|
|
2265
|
-
});
|
|
2266
|
-
});
|
|
2267
|
-
it('should keep initial data when the query key changes', async () => {
|
|
2268
|
-
const key = queryKey();
|
|
2269
|
-
const states = [];
|
|
2270
|
-
function Page() {
|
|
2271
|
-
const [count, setCount] = createSignal(0);
|
|
2272
|
-
const state = createQuery(() => ({
|
|
2273
|
-
queryKey: [key, count()],
|
|
2274
|
-
queryFn: () => ({ count: 10 }),
|
|
2275
|
-
staleTime: Infinity,
|
|
2276
|
-
initialData: () => ({ count: count() }),
|
|
2277
|
-
reconcile: false,
|
|
2278
|
-
}));
|
|
2279
|
-
createRenderEffect(() => {
|
|
2280
|
-
states.push({ ...state });
|
|
2281
|
-
});
|
|
2282
|
-
createEffect(() => {
|
|
2283
|
-
setActTimeout(() => {
|
|
2284
|
-
setCount(1);
|
|
2285
|
-
}, 10);
|
|
2286
|
-
}, []);
|
|
2287
|
-
return null;
|
|
2288
|
-
}
|
|
2289
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2290
|
-
<Page />
|
|
2291
|
-
</QueryClientProvider>));
|
|
2292
|
-
await sleep(100);
|
|
2293
|
-
expect(states.length).toBe(2);
|
|
2294
|
-
// Initial
|
|
2295
|
-
expect(states[0]).toMatchObject({ data: { count: 0 } });
|
|
2296
|
-
// Set state
|
|
2297
|
-
expect(states[1]).toMatchObject({ data: { count: 1 } });
|
|
2298
|
-
});
|
|
2299
|
-
it('should retry specified number of times', async () => {
|
|
2300
|
-
const key = queryKey();
|
|
2301
|
-
const queryFn = vi.fn();
|
|
2302
|
-
queryFn.mockImplementation(() => {
|
|
2303
|
-
return Promise.reject(new Error('Error test Barrett'));
|
|
2304
|
-
});
|
|
2305
|
-
function Page() {
|
|
2306
|
-
const state = createQuery(() => ({
|
|
2307
|
-
queryKey: key,
|
|
2308
|
-
queryFn,
|
|
2309
|
-
retry: 1,
|
|
2310
|
-
retryDelay: 1,
|
|
2311
|
-
}));
|
|
2312
|
-
return (<div>
|
|
2313
|
-
<h1>{state.status}</h1>
|
|
2314
|
-
<h2>Failed {state.failureCount} times</h2>
|
|
2315
|
-
<h2>Failed because {state.failureReason?.message}</h2>
|
|
2316
|
-
</div>);
|
|
2317
|
-
}
|
|
2318
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2319
|
-
<Page />
|
|
2320
|
-
</QueryClientProvider>));
|
|
2321
|
-
await waitFor(() => screen.getByText('pending'));
|
|
2322
|
-
await waitFor(() => screen.getByText('error'));
|
|
2323
|
-
// query should fail `retry + 1` times, since first time isn't a "retry"
|
|
2324
|
-
await waitFor(() => screen.getByText('Failed 2 times'));
|
|
2325
|
-
await waitFor(() => screen.getByText('Failed because Error test Barrett'));
|
|
2326
|
-
expect(queryFn).toHaveBeenCalledTimes(2);
|
|
2327
|
-
});
|
|
2328
|
-
it('should not retry if retry function `false`', async () => {
|
|
2329
|
-
const key = queryKey();
|
|
2330
|
-
const queryFn = vi.fn();
|
|
2331
|
-
queryFn.mockImplementationOnce(() => {
|
|
2332
|
-
return Promise.reject(new Error('Error test Tanner'));
|
|
2333
|
-
});
|
|
2334
|
-
queryFn.mockImplementation(() => {
|
|
2335
|
-
return Promise.reject(new Error('NoRetry'));
|
|
2336
|
-
});
|
|
2337
|
-
function Page() {
|
|
2338
|
-
const state = createQuery(() => ({
|
|
2339
|
-
queryKey: key,
|
|
2340
|
-
queryFn,
|
|
2341
|
-
retryDelay: 1,
|
|
2342
|
-
retry: (_failureCount, err) => err.message !== 'NoRetry',
|
|
2343
|
-
}));
|
|
2344
|
-
return (<div>
|
|
2345
|
-
<h1>{state.status}</h1>
|
|
2346
|
-
<h2>Failed {state.failureCount} times</h2>
|
|
2347
|
-
<h2>Failed because {state.failureReason?.message}</h2>
|
|
2348
|
-
<h2>{state.error?.message}</h2>
|
|
2349
|
-
</div>);
|
|
2350
|
-
}
|
|
2351
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2352
|
-
<Page />
|
|
2353
|
-
</QueryClientProvider>));
|
|
2354
|
-
await waitFor(() => screen.getByText('pending'));
|
|
2355
|
-
await waitFor(() => screen.getByText('error'));
|
|
2356
|
-
await waitFor(() => screen.getByText('Failed 2 times'));
|
|
2357
|
-
await waitFor(() => screen.getByText('Failed because NoRetry'));
|
|
2358
|
-
await waitFor(() => screen.getByText('NoRetry'));
|
|
2359
|
-
expect(queryFn).toHaveBeenCalledTimes(2);
|
|
2360
|
-
});
|
|
2361
|
-
it('should extract retryDelay from error', async () => {
|
|
2362
|
-
const key = queryKey();
|
|
2363
|
-
const queryFn = vi.fn();
|
|
2364
|
-
queryFn.mockImplementation(() => {
|
|
2365
|
-
return Promise.reject({ delay: 50 });
|
|
2366
|
-
});
|
|
2367
|
-
function Page() {
|
|
2368
|
-
const state = createQuery(() => ({
|
|
2369
|
-
queryKey: key,
|
|
2370
|
-
queryFn,
|
|
2371
|
-
retry: 1,
|
|
2372
|
-
retryDelay: (_, error) => error.delay,
|
|
2373
|
-
}));
|
|
2374
|
-
return (<div>
|
|
2375
|
-
<h1>{state.status}</h1>
|
|
2376
|
-
<h2>Failed {state.failureCount} times</h2>
|
|
2377
|
-
<h2>Failed because DelayError: {state.failureReason?.delay}ms</h2>
|
|
2378
|
-
</div>);
|
|
2379
|
-
}
|
|
2380
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2381
|
-
<Page />
|
|
2382
|
-
</QueryClientProvider>));
|
|
2383
|
-
await sleep(10);
|
|
2384
|
-
expect(queryFn).toHaveBeenCalledTimes(1);
|
|
2385
|
-
await waitFor(() => screen.getByText('Failed because DelayError: 50ms'));
|
|
2386
|
-
await waitFor(() => screen.getByText('Failed 2 times'));
|
|
2387
|
-
expect(queryFn).toHaveBeenCalledTimes(2);
|
|
2388
|
-
});
|
|
2389
|
-
// See https://github.com/tannerlinsley/react-query/issues/160
|
|
2390
|
-
it('should continue retry after focus regain', async () => {
|
|
2391
|
-
const key = queryKey();
|
|
2392
|
-
// make page unfocused
|
|
2393
|
-
const visibilityMock = mockVisibilityState('hidden');
|
|
2394
|
-
let count = 0;
|
|
2395
|
-
function Page() {
|
|
2396
|
-
const query = createQuery(() => ({
|
|
2397
|
-
queryKey: key,
|
|
2398
|
-
queryFn: () => {
|
|
2399
|
-
count++;
|
|
2400
|
-
return Promise.reject(`fetching error ${count}`);
|
|
2401
|
-
},
|
|
2402
|
-
retry: 3,
|
|
2403
|
-
retryDelay: 1,
|
|
2404
|
-
}));
|
|
2405
|
-
return (<div>
|
|
2406
|
-
<div>error {String(query.error)}</div>
|
|
2407
|
-
<div>status {query.status}</div>
|
|
2408
|
-
<div>failureCount {query.failureCount}</div>
|
|
2409
|
-
<div>failureReason {query.failureReason}</div>
|
|
2410
|
-
</div>);
|
|
2411
|
-
}
|
|
2412
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2413
|
-
<Page />
|
|
2414
|
-
</QueryClientProvider>));
|
|
2415
|
-
// The query should display the first error result
|
|
2416
|
-
await waitFor(() => screen.getByText('failureCount 1'));
|
|
2417
|
-
await waitFor(() => screen.getByText('failureReason fetching error 1'));
|
|
2418
|
-
await waitFor(() => screen.getByText('status pending'));
|
|
2419
|
-
await waitFor(() => screen.getByText('error null'));
|
|
2420
|
-
// Check if the query really paused
|
|
2421
|
-
await sleep(10);
|
|
2422
|
-
await waitFor(() => screen.getByText('failureCount 1'));
|
|
2423
|
-
await waitFor(() => screen.getByText('failureReason fetching error 1'));
|
|
2424
|
-
visibilityMock.mockRestore();
|
|
2425
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
2426
|
-
// Wait for the final result
|
|
2427
|
-
await waitFor(() => screen.getByText('failureCount 4'));
|
|
2428
|
-
await waitFor(() => screen.getByText('failureReason fetching error 4'));
|
|
2429
|
-
await waitFor(() => screen.getByText('status error'));
|
|
2430
|
-
await waitFor(() => screen.getByText('error fetching error 4'));
|
|
2431
|
-
// Check if the query really stopped
|
|
2432
|
-
await sleep(10);
|
|
2433
|
-
await waitFor(() => screen.getByText('failureCount 4'));
|
|
2434
|
-
await waitFor(() => screen.getByText('failureReason fetching error 4'));
|
|
2435
|
-
});
|
|
2436
|
-
it('should fetch on mount when a query was already created with setQueryData', async () => {
|
|
2437
|
-
const key = queryKey();
|
|
2438
|
-
const states = [];
|
|
2439
|
-
queryClient.setQueryData(key, 'prefetched');
|
|
2440
|
-
function Page() {
|
|
2441
|
-
const state = createQuery(() => ({
|
|
2442
|
-
queryKey: key,
|
|
2443
|
-
queryFn: () => 'data',
|
|
2444
|
-
}));
|
|
2445
|
-
createRenderEffect(() => {
|
|
2446
|
-
states.push({ ...state });
|
|
2447
|
-
});
|
|
2448
|
-
return null;
|
|
2449
|
-
}
|
|
2450
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2451
|
-
<Page />
|
|
2452
|
-
</QueryClientProvider>));
|
|
2453
|
-
await sleep(10);
|
|
2454
|
-
expect(states.length).toBe(2);
|
|
2455
|
-
expect(states).toMatchObject([
|
|
2456
|
-
{
|
|
2457
|
-
data: 'prefetched',
|
|
2458
|
-
isFetching: true,
|
|
2459
|
-
isStale: true,
|
|
2460
|
-
},
|
|
2461
|
-
{
|
|
2462
|
-
data: 'data',
|
|
2463
|
-
isFetching: false,
|
|
2464
|
-
isStale: true,
|
|
2465
|
-
},
|
|
2466
|
-
]);
|
|
2467
|
-
});
|
|
2468
|
-
it('should refetch after focus regain', async () => {
|
|
2469
|
-
const key = queryKey();
|
|
2470
|
-
const states = [];
|
|
2471
|
-
// make page unfocused
|
|
2472
|
-
const visibilityMock = mockVisibilityState('hidden');
|
|
2473
|
-
// set data in cache to check if the hook query fn is actually called
|
|
2474
|
-
queryClient.setQueryData(key, 'prefetched');
|
|
2475
|
-
function Page() {
|
|
2476
|
-
const state = createQuery(() => ({
|
|
2477
|
-
queryKey: key,
|
|
2478
|
-
queryFn: async () => {
|
|
2479
|
-
await sleep(10);
|
|
2480
|
-
return 'data';
|
|
2481
|
-
},
|
|
2482
|
-
}));
|
|
2483
|
-
createRenderEffect(() => {
|
|
2484
|
-
states.push({ ...state });
|
|
2485
|
-
});
|
|
2486
|
-
return (<div>
|
|
2487
|
-
{state.data}, {state.isStale}, {state.isFetching}
|
|
2488
|
-
</div>);
|
|
2489
|
-
}
|
|
2490
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2491
|
-
<Page />
|
|
2492
|
-
</QueryClientProvider>));
|
|
2493
|
-
await waitFor(() => expect(states.length).toBe(2));
|
|
2494
|
-
// reset visibilityState to original value
|
|
2495
|
-
visibilityMock.mockRestore();
|
|
2496
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
2497
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
2498
|
-
expect(states).toMatchObject([
|
|
2499
|
-
{
|
|
2500
|
-
data: 'prefetched',
|
|
2501
|
-
isFetching: true,
|
|
2502
|
-
isStale: true,
|
|
2503
|
-
},
|
|
2504
|
-
{
|
|
2505
|
-
data: 'data',
|
|
2506
|
-
isFetching: false,
|
|
2507
|
-
isStale: true,
|
|
2508
|
-
},
|
|
2509
|
-
{
|
|
2510
|
-
data: 'data',
|
|
2511
|
-
isFetching: true,
|
|
2512
|
-
isStale: true,
|
|
2513
|
-
},
|
|
2514
|
-
{
|
|
2515
|
-
data: 'data',
|
|
2516
|
-
isFetching: false,
|
|
2517
|
-
isStale: true,
|
|
2518
|
-
},
|
|
2519
|
-
]);
|
|
2520
|
-
});
|
|
2521
|
-
// See https://github.com/tannerlinsley/react-query/issues/195
|
|
2522
|
-
it('should refetch if stale after a prefetch', async () => {
|
|
2523
|
-
const key = queryKey();
|
|
2524
|
-
const states = [];
|
|
2525
|
-
const queryFn = vi.fn();
|
|
2526
|
-
queryFn.mockImplementation(() => 'data');
|
|
2527
|
-
const prefetchQueryFn = vi.fn();
|
|
2528
|
-
prefetchQueryFn.mockImplementation(() => 'not yet...');
|
|
2529
|
-
await queryClient.prefetchQuery({
|
|
2530
|
-
queryKey: key,
|
|
2531
|
-
queryFn: prefetchQueryFn,
|
|
2532
|
-
staleTime: 10,
|
|
2533
|
-
});
|
|
2534
|
-
await sleep(11);
|
|
2535
|
-
function Page() {
|
|
2536
|
-
const state = createQuery(() => ({ queryKey: key, queryFn }));
|
|
2537
|
-
createRenderEffect(() => {
|
|
2538
|
-
states.push({ ...state });
|
|
2539
|
-
});
|
|
2540
|
-
return null;
|
|
2541
|
-
}
|
|
2542
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2543
|
-
<Page />
|
|
2544
|
-
</QueryClientProvider>));
|
|
2545
|
-
await waitFor(() => expect(states.length).toBe(2));
|
|
2546
|
-
expect(prefetchQueryFn).toHaveBeenCalledTimes(1);
|
|
2547
|
-
expect(queryFn).toHaveBeenCalledTimes(1);
|
|
2548
|
-
});
|
|
2549
|
-
it('should not refetch if not stale after a prefetch', async () => {
|
|
2550
|
-
const key = queryKey();
|
|
2551
|
-
const queryFn = vi.fn();
|
|
2552
|
-
queryFn.mockImplementation(() => 'data');
|
|
2553
|
-
const prefetchQueryFn = vi.fn();
|
|
2554
|
-
prefetchQueryFn.mockImplementation(async () => {
|
|
2555
|
-
await sleep(10);
|
|
2556
|
-
return 'not yet...';
|
|
2557
|
-
});
|
|
2558
|
-
await queryClient.prefetchQuery({
|
|
2559
|
-
queryKey: key,
|
|
2560
|
-
queryFn: prefetchQueryFn,
|
|
2561
|
-
staleTime: 1000,
|
|
2562
|
-
});
|
|
2563
|
-
await sleep(0);
|
|
2564
|
-
function Page() {
|
|
2565
|
-
createQuery(() => ({ queryKey: key, queryFn, staleTime: 1000 }));
|
|
2566
|
-
return null;
|
|
2567
|
-
}
|
|
2568
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2569
|
-
<Page />
|
|
2570
|
-
</QueryClientProvider>));
|
|
2571
|
-
await sleep(0);
|
|
2572
|
-
expect(prefetchQueryFn).toHaveBeenCalledTimes(1);
|
|
2573
|
-
expect(queryFn).toHaveBeenCalledTimes(0);
|
|
2574
|
-
});
|
|
2575
|
-
// See https://github.com/tannerlinsley/react-query/issues/190
|
|
2576
|
-
it('should reset failureCount and failureReason on successful fetch', async () => {
|
|
2577
|
-
const key = queryKey();
|
|
2578
|
-
function Page() {
|
|
2579
|
-
let counter = 0;
|
|
2580
|
-
const query = createQuery(() => ({
|
|
2581
|
-
queryKey: key,
|
|
2582
|
-
queryFn: async () => {
|
|
2583
|
-
if (counter < 2) {
|
|
2584
|
-
counter++;
|
|
2585
|
-
throw new Error('error');
|
|
2586
|
-
}
|
|
2587
|
-
else {
|
|
2588
|
-
return 'data';
|
|
2589
|
-
}
|
|
2590
|
-
},
|
|
2591
|
-
retryDelay: 10,
|
|
2592
|
-
}));
|
|
2593
|
-
return (<div>
|
|
2594
|
-
<div>failureCount {query.failureCount}</div>
|
|
2595
|
-
<div>failureReason {query.failureReason?.message ?? 'null'}</div>
|
|
2596
|
-
</div>);
|
|
2597
|
-
}
|
|
2598
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2599
|
-
<Page />
|
|
2600
|
-
</QueryClientProvider>));
|
|
2601
|
-
await waitFor(() => screen.getByText('failureCount 2'));
|
|
2602
|
-
await waitFor(() => screen.getByText('failureReason error'));
|
|
2603
|
-
await waitFor(() => screen.getByText('failureCount 0'));
|
|
2604
|
-
await waitFor(() => screen.getByText('failureReason null'));
|
|
2605
|
-
});
|
|
2606
|
-
// See https://github.com/tannerlinsley/react-query/issues/199
|
|
2607
|
-
it('should use prefetched data for dependent query', async () => {
|
|
2608
|
-
const key = queryKey();
|
|
2609
|
-
let count = 0;
|
|
2610
|
-
function Page() {
|
|
2611
|
-
const [enabled, setEnabled] = createSignal(false);
|
|
2612
|
-
const [isPrefetched, setPrefetched] = createSignal(false);
|
|
2613
|
-
const query = createQuery(() => ({
|
|
2614
|
-
queryKey: key,
|
|
2615
|
-
queryFn: async () => {
|
|
2616
|
-
count++;
|
|
2617
|
-
await sleep(10);
|
|
2618
|
-
return count;
|
|
2619
|
-
},
|
|
2620
|
-
enabled: enabled(),
|
|
2621
|
-
}));
|
|
2622
|
-
createEffect(() => {
|
|
2623
|
-
async function prefetch() {
|
|
2624
|
-
await queryClient.prefetchQuery({
|
|
2625
|
-
queryKey: key,
|
|
2626
|
-
queryFn: () => Promise.resolve('prefetched data'),
|
|
2627
|
-
});
|
|
2628
|
-
setPrefetched(true);
|
|
2629
|
-
}
|
|
2630
|
-
prefetch();
|
|
2631
|
-
});
|
|
2632
|
-
return (<div>
|
|
2633
|
-
{isPrefetched() && <div>isPrefetched</div>}
|
|
2634
|
-
<button onClick={() => setEnabled(true)}>setKey</button>
|
|
2635
|
-
<div>data: {query.data}</div>
|
|
2636
|
-
</div>);
|
|
2637
|
-
}
|
|
2638
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2639
|
-
<Page />
|
|
2640
|
-
</QueryClientProvider>));
|
|
2641
|
-
await waitFor(() => screen.getByText('isPrefetched'));
|
|
2642
|
-
fireEvent.click(screen.getByText('setKey'));
|
|
2643
|
-
await waitFor(() => screen.getByText('data: prefetched data'));
|
|
2644
|
-
await waitFor(() => screen.getByText('data: 1'));
|
|
2645
|
-
expect(count).toBe(1);
|
|
2646
|
-
});
|
|
2647
|
-
it('should support dependent queries via the enable config option', async () => {
|
|
2648
|
-
const key = queryKey();
|
|
2649
|
-
function Page() {
|
|
2650
|
-
const [shouldFetch, setShouldFetch] = createSignal(false);
|
|
2651
|
-
const query = createQuery(() => ({
|
|
2652
|
-
queryKey: key,
|
|
2653
|
-
queryFn: () => 'data',
|
|
2654
|
-
enabled: shouldFetch(),
|
|
2655
|
-
}));
|
|
2656
|
-
return (<div>
|
|
2657
|
-
<div>FetchStatus: {query.fetchStatus}</div>
|
|
2658
|
-
<h2>Data: {query.data || 'no data'}</h2>
|
|
2659
|
-
{query.isStale ? (<button onClick={() => setShouldFetch(true)}>fetch</button>) : null}
|
|
2660
|
-
</div>);
|
|
2661
|
-
}
|
|
2662
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2663
|
-
<Page />
|
|
2664
|
-
</QueryClientProvider>));
|
|
2665
|
-
screen.getByText('FetchStatus: idle');
|
|
2666
|
-
screen.getByText('Data: no data');
|
|
2667
|
-
fireEvent.click(screen.getByText('fetch'));
|
|
2668
|
-
await waitFor(() => screen.getByText('FetchStatus: fetching'));
|
|
2669
|
-
await waitFor(() => [
|
|
2670
|
-
screen.getByText('FetchStatus: idle'),
|
|
2671
|
-
screen.getByText('Data: data'),
|
|
2672
|
-
]);
|
|
2673
|
-
});
|
|
2674
|
-
it('should mark query as fetching, when using initialData', async () => {
|
|
2675
|
-
const key = queryKey();
|
|
2676
|
-
const results = [];
|
|
2677
|
-
function Page() {
|
|
2678
|
-
const result = createQuery(() => ({
|
|
2679
|
-
queryKey: key,
|
|
2680
|
-
queryFn: async () => {
|
|
2681
|
-
await sleep(10);
|
|
2682
|
-
return 'serverData';
|
|
2683
|
-
},
|
|
2684
|
-
initialData: 'initialData',
|
|
2685
|
-
}));
|
|
2686
|
-
createRenderEffect(() => {
|
|
2687
|
-
results.push({ ...result });
|
|
2688
|
-
});
|
|
2689
|
-
return <div>data: {result.data}</div>;
|
|
2690
|
-
}
|
|
2691
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2692
|
-
<Page />
|
|
2693
|
-
</QueryClientProvider>));
|
|
2694
|
-
await waitFor(() => screen.getByText('data: initialData'));
|
|
2695
|
-
await waitFor(() => screen.getByText('data: serverData'));
|
|
2696
|
-
expect(results.length).toBe(2);
|
|
2697
|
-
expect(results[0]).toMatchObject({ data: 'initialData', isFetching: true });
|
|
2698
|
-
expect(results[1]).toMatchObject({ data: 'serverData', isFetching: false });
|
|
2699
|
-
});
|
|
2700
|
-
it('should initialize state properly, when initialData is falsy', async () => {
|
|
2701
|
-
const key = queryKey();
|
|
2702
|
-
const results = [];
|
|
2703
|
-
function Page() {
|
|
2704
|
-
const result = createQuery(() => ({
|
|
2705
|
-
queryKey: key,
|
|
2706
|
-
queryFn: () => 1,
|
|
2707
|
-
initialData: 0,
|
|
2708
|
-
}));
|
|
2709
|
-
createRenderEffect(() => {
|
|
2710
|
-
results.push({ ...result });
|
|
2711
|
-
});
|
|
2712
|
-
return null;
|
|
2713
|
-
}
|
|
2714
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2715
|
-
<Page />
|
|
2716
|
-
</QueryClientProvider>));
|
|
2717
|
-
await sleep(10);
|
|
2718
|
-
expect(results.length).toBe(2);
|
|
2719
|
-
expect(results[0]).toMatchObject({ data: 0, isFetching: true });
|
|
2720
|
-
expect(results[1]).toMatchObject({ data: 1, isFetching: false });
|
|
2721
|
-
});
|
|
2722
|
-
// // See https://github.com/tannerlinsley/react-query/issues/214
|
|
2723
|
-
it('data should persist when enabled is changed to false', async () => {
|
|
2724
|
-
const key = queryKey();
|
|
2725
|
-
const results = [];
|
|
2726
|
-
function Page() {
|
|
2727
|
-
const [shouldFetch, setShouldFetch] = createSignal(true);
|
|
2728
|
-
const result = createQuery(() => ({
|
|
2729
|
-
queryKey: key,
|
|
2730
|
-
queryFn: () => 'fetched data',
|
|
2731
|
-
enabled: shouldFetch(),
|
|
2732
|
-
initialData: shouldFetch() ? 'initial' : 'initial falsy',
|
|
2733
|
-
}));
|
|
2734
|
-
createRenderEffect(() => {
|
|
2735
|
-
results.push({ ...result });
|
|
2736
|
-
});
|
|
2737
|
-
createEffect(() => {
|
|
2738
|
-
setActTimeout(() => {
|
|
2739
|
-
setShouldFetch(false);
|
|
2740
|
-
}, 5);
|
|
2741
|
-
});
|
|
2742
|
-
return null;
|
|
2743
|
-
}
|
|
2744
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2745
|
-
<Page />
|
|
2746
|
-
</QueryClientProvider>));
|
|
2747
|
-
await sleep(50);
|
|
2748
|
-
expect(results.length).toBe(2);
|
|
2749
|
-
expect(results[0]).toMatchObject({ data: 'initial', isStale: true });
|
|
2750
|
-
expect(results[1]).toMatchObject({ data: 'fetched data', isStale: true });
|
|
2751
|
-
// Wont render 3rd time, because data is still the same
|
|
2752
|
-
});
|
|
2753
|
-
it('it should support enabled:false in query object syntax', async () => {
|
|
2754
|
-
const key = queryKey();
|
|
2755
|
-
const queryFn = vi.fn();
|
|
2756
|
-
queryFn.mockImplementation(() => 'data');
|
|
2757
|
-
function Page() {
|
|
2758
|
-
const { fetchStatus } = createQuery(() => ({
|
|
2759
|
-
queryKey: key,
|
|
2760
|
-
queryFn,
|
|
2761
|
-
enabled: false,
|
|
2762
|
-
}));
|
|
2763
|
-
return <div>fetchStatus: {fetchStatus}</div>;
|
|
2764
|
-
}
|
|
2765
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2766
|
-
<Page />
|
|
2767
|
-
</QueryClientProvider>));
|
|
2768
|
-
expect(queryFn).not.toHaveBeenCalled();
|
|
2769
|
-
expect(queryCache.find({ queryKey: key })).not.toBeUndefined();
|
|
2770
|
-
screen.getByText('fetchStatus: idle');
|
|
2771
|
-
});
|
|
2772
|
-
// See https://github.com/tannerlinsley/react-query/issues/360
|
|
2773
|
-
it('should init to status:pending, fetchStatus:idle when enabled is false', async () => {
|
|
2774
|
-
const key = queryKey();
|
|
2775
|
-
function Page() {
|
|
2776
|
-
const query = createQuery(() => ({
|
|
2777
|
-
queryKey: key,
|
|
2778
|
-
queryFn: () => 'data',
|
|
2779
|
-
enabled: false,
|
|
2780
|
-
}));
|
|
2781
|
-
return (<div>
|
|
2782
|
-
<div>
|
|
2783
|
-
status: {query.status}, {query.fetchStatus}
|
|
2784
|
-
</div>
|
|
2785
|
-
</div>);
|
|
2786
|
-
}
|
|
2787
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2788
|
-
<Page />
|
|
2789
|
-
</QueryClientProvider>));
|
|
2790
|
-
await waitFor(() => screen.getByText('status: pending, idle'));
|
|
2791
|
-
});
|
|
2792
|
-
it('should not schedule garbage collection, if gcTimeout is set to `Infinity`', async () => {
|
|
2793
|
-
const key = queryKey();
|
|
2794
|
-
function Page() {
|
|
2795
|
-
const query = createQuery(() => ({
|
|
2796
|
-
queryKey: key,
|
|
2797
|
-
queryFn: () => 'fetched data',
|
|
2798
|
-
gcTime: Infinity,
|
|
2799
|
-
}));
|
|
2800
|
-
return <div>{query.data}</div>;
|
|
2801
|
-
}
|
|
2802
|
-
const result = render(() => (<QueryClientProvider client={queryClient}>
|
|
2803
|
-
<Page />
|
|
2804
|
-
</QueryClientProvider>));
|
|
2805
|
-
await waitFor(() => screen.getByText('fetched data'));
|
|
2806
|
-
const setTimeoutSpy = vi.spyOn(window, 'setTimeout');
|
|
2807
|
-
result.unmount();
|
|
2808
|
-
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
|
2809
|
-
});
|
|
2810
|
-
it('should schedule garbage collection, if gcTimeout is not set to `Infinity`', async () => {
|
|
2811
|
-
const key = queryKey();
|
|
2812
|
-
function Page() {
|
|
2813
|
-
const query = createQuery(() => ({
|
|
2814
|
-
queryKey: key,
|
|
2815
|
-
queryFn: () => 'fetched data',
|
|
2816
|
-
gcTime: 1000 * 60 * 10, //10 Minutes
|
|
2817
|
-
}));
|
|
2818
|
-
return <div>{query.data}</div>;
|
|
2819
|
-
}
|
|
2820
|
-
const result = render(() => (<QueryClientProvider client={queryClient}>
|
|
2821
|
-
<Page />
|
|
2822
|
-
</QueryClientProvider>));
|
|
2823
|
-
await waitFor(() => screen.getByText('fetched data'));
|
|
2824
|
-
const setTimeoutSpy = vi.spyOn(window, 'setTimeout');
|
|
2825
|
-
result.unmount();
|
|
2826
|
-
expect(setTimeoutSpy).toHaveBeenLastCalledWith(expect.any(Function), 1000 * 60 * 10);
|
|
2827
|
-
});
|
|
2828
|
-
it('should not cause memo churn when data does not change', async () => {
|
|
2829
|
-
const key = queryKey();
|
|
2830
|
-
const queryFn = vi.fn().mockReturnValue('data');
|
|
2831
|
-
const memoFn = vi.fn();
|
|
2832
|
-
function Page() {
|
|
2833
|
-
const result = createQuery(() => ({
|
|
2834
|
-
queryKey: key,
|
|
2835
|
-
queryFn: async () => {
|
|
2836
|
-
await sleep(10);
|
|
2837
|
-
return (queryFn() || {
|
|
2838
|
-
data: {
|
|
2839
|
-
nested: true,
|
|
2840
|
-
},
|
|
2841
|
-
});
|
|
2842
|
-
},
|
|
2843
|
-
}));
|
|
2844
|
-
createMemo(() => {
|
|
2845
|
-
memoFn();
|
|
2846
|
-
return result.data;
|
|
2847
|
-
});
|
|
2848
|
-
return (<div>
|
|
2849
|
-
<div>status {result.status}</div>
|
|
2850
|
-
<div>isFetching {result.isFetching ? 'true' : 'false'}</div>
|
|
2851
|
-
<button onClick={() => result.refetch()}>refetch</button>
|
|
2852
|
-
</div>);
|
|
2853
|
-
}
|
|
2854
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2855
|
-
<Page />
|
|
2856
|
-
</QueryClientProvider>));
|
|
2857
|
-
await waitFor(() => screen.getByText('status pending'));
|
|
2858
|
-
await waitFor(() => screen.getByText('status success'));
|
|
2859
|
-
fireEvent.click(screen.getByText('refetch'));
|
|
2860
|
-
await waitFor(() => screen.getByText('isFetching true'));
|
|
2861
|
-
await waitFor(() => screen.getByText('isFetching false'));
|
|
2862
|
-
expect(queryFn).toHaveBeenCalledTimes(2);
|
|
2863
|
-
expect(memoFn).toHaveBeenCalledTimes(2);
|
|
2864
|
-
});
|
|
2865
|
-
it('should update data upon interval changes', async () => {
|
|
2866
|
-
const key = queryKey();
|
|
2867
|
-
let count = 0;
|
|
2868
|
-
function Page() {
|
|
2869
|
-
const [int, setInt] = createSignal(200);
|
|
2870
|
-
const state = createQuery(() => ({
|
|
2871
|
-
queryKey: key,
|
|
2872
|
-
queryFn: () => count++,
|
|
2873
|
-
refetchInterval: int(),
|
|
2874
|
-
}));
|
|
2875
|
-
createEffect(() => {
|
|
2876
|
-
if (state.data === 2) {
|
|
2877
|
-
setInt(0);
|
|
2878
|
-
}
|
|
2879
|
-
});
|
|
2880
|
-
return <div>count: {state.data}</div>;
|
|
2881
|
-
}
|
|
2882
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2883
|
-
<Page />
|
|
2884
|
-
</QueryClientProvider>));
|
|
2885
|
-
// mount
|
|
2886
|
-
await waitFor(() => screen.getByText('count: 0'));
|
|
2887
|
-
await waitFor(() => screen.getByText('count: 1'));
|
|
2888
|
-
await waitFor(() => screen.getByText('count: 2'));
|
|
2889
|
-
});
|
|
2890
|
-
it('should refetch in an interval depending on function result', async () => {
|
|
2891
|
-
const key = queryKey();
|
|
2892
|
-
let count = 0;
|
|
2893
|
-
const states = [];
|
|
2894
|
-
function Page() {
|
|
2895
|
-
const state = createQuery(() => ({
|
|
2896
|
-
queryKey: key,
|
|
2897
|
-
queryFn: async () => {
|
|
2898
|
-
await sleep(10);
|
|
2899
|
-
return count++;
|
|
2900
|
-
},
|
|
2901
|
-
refetchInterval: (data = 0) => (data < 2 ? 10 : false),
|
|
2902
|
-
}));
|
|
2903
|
-
createRenderEffect(() => {
|
|
2904
|
-
states.push({ ...state });
|
|
2905
|
-
});
|
|
2906
|
-
return (<div>
|
|
2907
|
-
<h1>count: {state.data}</h1>
|
|
2908
|
-
<h2>status: {state.status}</h2>
|
|
2909
|
-
<h2>data: {state.data}</h2>
|
|
2910
|
-
<h2>refetch: {state.isRefetching}</h2>
|
|
2911
|
-
</div>);
|
|
2912
|
-
}
|
|
2913
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2914
|
-
<Page />
|
|
2915
|
-
</QueryClientProvider>));
|
|
2916
|
-
await waitFor(() => screen.getByText('count: 2'));
|
|
2917
|
-
expect(states.length).toEqual(6);
|
|
2918
|
-
expect(states).toMatchObject([
|
|
2919
|
-
{
|
|
2920
|
-
status: 'pending',
|
|
2921
|
-
isFetching: true,
|
|
2922
|
-
data: undefined,
|
|
2923
|
-
},
|
|
2924
|
-
{
|
|
2925
|
-
status: 'success',
|
|
2926
|
-
isFetching: false,
|
|
2927
|
-
data: 0,
|
|
2928
|
-
},
|
|
2929
|
-
{
|
|
2930
|
-
status: 'success',
|
|
2931
|
-
isFetching: true,
|
|
2932
|
-
data: 0,
|
|
2933
|
-
},
|
|
2934
|
-
{
|
|
2935
|
-
status: 'success',
|
|
2936
|
-
isFetching: false,
|
|
2937
|
-
data: 1,
|
|
2938
|
-
},
|
|
2939
|
-
{
|
|
2940
|
-
status: 'success',
|
|
2941
|
-
isFetching: true,
|
|
2942
|
-
data: 1,
|
|
2943
|
-
},
|
|
2944
|
-
{
|
|
2945
|
-
status: 'success',
|
|
2946
|
-
isFetching: false,
|
|
2947
|
-
data: 2,
|
|
2948
|
-
},
|
|
2949
|
-
]);
|
|
2950
|
-
});
|
|
2951
|
-
it('should not interval fetch with a refetchInterval of 0', async () => {
|
|
2952
|
-
const key = queryKey();
|
|
2953
|
-
const states = [];
|
|
2954
|
-
function Page() {
|
|
2955
|
-
const state = createQuery(() => ({
|
|
2956
|
-
queryKey: key,
|
|
2957
|
-
queryFn: () => 1,
|
|
2958
|
-
refetchInterval: 0,
|
|
2959
|
-
}));
|
|
2960
|
-
createRenderEffect(() => {
|
|
2961
|
-
states.push({ ...state });
|
|
2962
|
-
});
|
|
2963
|
-
return <div>count: {state.data}</div>;
|
|
2964
|
-
}
|
|
2965
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2966
|
-
<Page />
|
|
2967
|
-
</QueryClientProvider>));
|
|
2968
|
-
await waitFor(() => screen.getByText('count: 1'));
|
|
2969
|
-
await sleep(10); //extra sleep to make sure we're not re-fetching
|
|
2970
|
-
expect(states.length).toEqual(2);
|
|
2971
|
-
expect(states).toMatchObject([
|
|
2972
|
-
{
|
|
2973
|
-
status: 'pending',
|
|
2974
|
-
isFetching: true,
|
|
2975
|
-
data: undefined,
|
|
2976
|
-
},
|
|
2977
|
-
{
|
|
2978
|
-
status: 'success',
|
|
2979
|
-
isFetching: false,
|
|
2980
|
-
data: 1,
|
|
2981
|
-
},
|
|
2982
|
-
]);
|
|
2983
|
-
});
|
|
2984
|
-
it('should accept an empty string as query key', async () => {
|
|
2985
|
-
function Page() {
|
|
2986
|
-
const result = createQuery(() => ({
|
|
2987
|
-
queryKey: [''],
|
|
2988
|
-
queryFn: (ctx) => ctx.queryKey,
|
|
2989
|
-
}));
|
|
2990
|
-
return <>{JSON.stringify(result.data)}</>;
|
|
2991
|
-
}
|
|
2992
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
2993
|
-
<Page />
|
|
2994
|
-
</QueryClientProvider>));
|
|
2995
|
-
await waitFor(() => screen.getByText(''));
|
|
2996
|
-
});
|
|
2997
|
-
it('should accept an object as query key', async () => {
|
|
2998
|
-
function Page() {
|
|
2999
|
-
const result = createQuery(() => ({
|
|
3000
|
-
queryKey: [{ a: 'a' }],
|
|
3001
|
-
queryFn: (ctx) => ctx.queryKey,
|
|
3002
|
-
}));
|
|
3003
|
-
return <>{JSON.stringify(result.data)}</>;
|
|
3004
|
-
}
|
|
3005
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3006
|
-
<Page />
|
|
3007
|
-
</QueryClientProvider>));
|
|
3008
|
-
await waitFor(() => screen.getByText('[{"a":"a"}]'));
|
|
3009
|
-
});
|
|
3010
|
-
it('should refetch if any query instance becomes enabled', async () => {
|
|
3011
|
-
const key = queryKey();
|
|
3012
|
-
const queryFn = vi.fn().mockReturnValue('data');
|
|
3013
|
-
function Disabled() {
|
|
3014
|
-
createQuery(() => ({ queryKey: key, queryFn, enabled: false }));
|
|
3015
|
-
return null;
|
|
3016
|
-
}
|
|
3017
|
-
function Page() {
|
|
3018
|
-
const [enabled, setEnabled] = createSignal(false);
|
|
3019
|
-
const result = createQuery(() => ({
|
|
3020
|
-
queryKey: key,
|
|
3021
|
-
queryFn,
|
|
3022
|
-
enabled: enabled(),
|
|
3023
|
-
}));
|
|
3024
|
-
return (<>
|
|
3025
|
-
<Disabled />
|
|
3026
|
-
<div>{result.data}</div>
|
|
3027
|
-
<button onClick={() => setEnabled(true)}>enable</button>
|
|
3028
|
-
</>);
|
|
3029
|
-
}
|
|
3030
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3031
|
-
<Page />
|
|
3032
|
-
</QueryClientProvider>));
|
|
3033
|
-
expect(queryFn).toHaveBeenCalledTimes(0);
|
|
3034
|
-
fireEvent.click(screen.getByText('enable'));
|
|
3035
|
-
await waitFor(() => screen.getByText('data'));
|
|
3036
|
-
expect(queryFn).toHaveBeenCalledTimes(1);
|
|
3037
|
-
});
|
|
3038
|
-
it('should use placeholder data while the query loads', async () => {
|
|
3039
|
-
const key1 = queryKey();
|
|
3040
|
-
const states = [];
|
|
3041
|
-
function Page() {
|
|
3042
|
-
const state = createQuery(() => ({
|
|
3043
|
-
queryKey: key1,
|
|
3044
|
-
queryFn: () => 'data',
|
|
3045
|
-
placeholderData: 'placeholder',
|
|
3046
|
-
}));
|
|
3047
|
-
createRenderEffect(() => {
|
|
3048
|
-
states.push({ ...state });
|
|
3049
|
-
});
|
|
3050
|
-
return (<div>
|
|
3051
|
-
<h2>Data: {state.data}</h2>
|
|
3052
|
-
<div>Status: {state.status}</div>
|
|
3053
|
-
</div>);
|
|
3054
|
-
}
|
|
3055
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3056
|
-
<Page />
|
|
3057
|
-
</QueryClientProvider>));
|
|
3058
|
-
await waitFor(() => screen.getByText('Data: data'));
|
|
3059
|
-
expect(states).toMatchObject([
|
|
3060
|
-
{
|
|
3061
|
-
isSuccess: true,
|
|
3062
|
-
isPlaceholderData: true,
|
|
3063
|
-
data: 'placeholder',
|
|
3064
|
-
},
|
|
3065
|
-
{
|
|
3066
|
-
isSuccess: true,
|
|
3067
|
-
isPlaceholderData: false,
|
|
3068
|
-
data: 'data',
|
|
3069
|
-
},
|
|
3070
|
-
]);
|
|
3071
|
-
});
|
|
3072
|
-
it('should use placeholder data even for disabled queries', async () => {
|
|
3073
|
-
const key1 = queryKey();
|
|
3074
|
-
const states = [];
|
|
3075
|
-
function Page() {
|
|
3076
|
-
const [count, setCount] = createSignal(0);
|
|
3077
|
-
const state = createQuery(() => ({
|
|
3078
|
-
queryKey: key1,
|
|
3079
|
-
queryFn: () => 'data',
|
|
3080
|
-
placeholderData: 'placeholder',
|
|
3081
|
-
enabled: count() === 0,
|
|
3082
|
-
}));
|
|
3083
|
-
createRenderEffect(() => {
|
|
3084
|
-
states.push({ state: { ...state }, count: count() });
|
|
3085
|
-
});
|
|
3086
|
-
createEffect(() => {
|
|
3087
|
-
setCount(1);
|
|
3088
|
-
});
|
|
3089
|
-
return (<div>
|
|
3090
|
-
<h2>Data: {state.data}</h2>
|
|
3091
|
-
<div>Status: {state.status}</div>
|
|
3092
|
-
</div>);
|
|
3093
|
-
}
|
|
3094
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3095
|
-
<Page />
|
|
3096
|
-
</QueryClientProvider>));
|
|
3097
|
-
await waitFor(() => screen.getByText('Data: data'));
|
|
3098
|
-
expect(states).toMatchObject([
|
|
3099
|
-
{
|
|
3100
|
-
state: {
|
|
3101
|
-
isSuccess: true,
|
|
3102
|
-
isPlaceholderData: true,
|
|
3103
|
-
data: 'placeholder',
|
|
3104
|
-
},
|
|
3105
|
-
count: 0,
|
|
3106
|
-
},
|
|
3107
|
-
{
|
|
3108
|
-
state: {
|
|
3109
|
-
isSuccess: true,
|
|
3110
|
-
isPlaceholderData: true,
|
|
3111
|
-
data: 'placeholder',
|
|
3112
|
-
},
|
|
3113
|
-
count: 1,
|
|
3114
|
-
},
|
|
3115
|
-
{
|
|
3116
|
-
state: {
|
|
3117
|
-
isSuccess: true,
|
|
3118
|
-
isPlaceholderData: false,
|
|
3119
|
-
data: 'data',
|
|
3120
|
-
},
|
|
3121
|
-
count: 1,
|
|
3122
|
-
},
|
|
3123
|
-
]);
|
|
3124
|
-
});
|
|
3125
|
-
it('placeholder data should run through select', async () => {
|
|
3126
|
-
const key1 = queryKey();
|
|
3127
|
-
const states = [];
|
|
3128
|
-
function Page() {
|
|
3129
|
-
const state = createQuery(() => ({
|
|
3130
|
-
queryKey: key1,
|
|
3131
|
-
queryFn: () => 1,
|
|
3132
|
-
placeholderData: 23,
|
|
3133
|
-
select: (data) => String(data * 2),
|
|
3134
|
-
}));
|
|
3135
|
-
createRenderEffect(() => {
|
|
3136
|
-
states.push({ ...state });
|
|
3137
|
-
});
|
|
3138
|
-
return (<div>
|
|
3139
|
-
<h2>Data: {state.data}</h2>
|
|
3140
|
-
<div>Status: {state.status}</div>
|
|
3141
|
-
</div>);
|
|
3142
|
-
}
|
|
3143
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3144
|
-
<Page />
|
|
3145
|
-
</QueryClientProvider>));
|
|
3146
|
-
await waitFor(() => screen.getByText('Data: 2'));
|
|
3147
|
-
expect(states).toMatchObject([
|
|
3148
|
-
{
|
|
3149
|
-
isSuccess: true,
|
|
3150
|
-
isPlaceholderData: true,
|
|
3151
|
-
data: '46',
|
|
3152
|
-
},
|
|
3153
|
-
{
|
|
3154
|
-
isSuccess: true,
|
|
3155
|
-
isPlaceholderData: false,
|
|
3156
|
-
data: '2',
|
|
3157
|
-
},
|
|
3158
|
-
]);
|
|
3159
|
-
});
|
|
3160
|
-
it('placeholder data function result should run through select', async () => {
|
|
3161
|
-
const key1 = queryKey();
|
|
3162
|
-
const states = [];
|
|
3163
|
-
let placeholderFunctionRunCount = 0;
|
|
3164
|
-
function Page() {
|
|
3165
|
-
const state = createQuery(() => ({
|
|
3166
|
-
queryKey: key1,
|
|
3167
|
-
queryFn: () => 1,
|
|
3168
|
-
placeholderData: () => {
|
|
3169
|
-
placeholderFunctionRunCount++;
|
|
3170
|
-
return 23;
|
|
3171
|
-
},
|
|
3172
|
-
select: (data) => String(data * 2),
|
|
3173
|
-
}));
|
|
3174
|
-
createRenderEffect(() => {
|
|
3175
|
-
states.push({ ...state });
|
|
3176
|
-
});
|
|
3177
|
-
return (<div>
|
|
3178
|
-
<h2>Data: {state.data}</h2>
|
|
3179
|
-
<div>Status: {state.status}</div>
|
|
3180
|
-
</div>);
|
|
3181
|
-
}
|
|
3182
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3183
|
-
<Page />
|
|
3184
|
-
</QueryClientProvider>));
|
|
3185
|
-
await waitFor(() => screen.getByText('Data: 2'));
|
|
3186
|
-
expect(states).toMatchObject([
|
|
3187
|
-
{
|
|
3188
|
-
isSuccess: true,
|
|
3189
|
-
isPlaceholderData: true,
|
|
3190
|
-
data: '46',
|
|
3191
|
-
},
|
|
3192
|
-
{
|
|
3193
|
-
isSuccess: true,
|
|
3194
|
-
isPlaceholderData: false,
|
|
3195
|
-
data: '2',
|
|
3196
|
-
},
|
|
3197
|
-
]);
|
|
3198
|
-
expect(placeholderFunctionRunCount).toEqual(1);
|
|
3199
|
-
});
|
|
3200
|
-
it('select should always return the correct state', async () => {
|
|
3201
|
-
const key1 = queryKey();
|
|
3202
|
-
function Page() {
|
|
3203
|
-
const [count, setCount] = createSignal(2);
|
|
3204
|
-
const [forceValue, setForceValue] = createSignal(1);
|
|
3205
|
-
const inc = () => {
|
|
3206
|
-
setCount((prev) => prev + 1);
|
|
3207
|
-
};
|
|
3208
|
-
const forceUpdate = () => {
|
|
3209
|
-
setForceValue((prev) => prev + 1);
|
|
3210
|
-
};
|
|
3211
|
-
const state = createQuery(() => ({
|
|
3212
|
-
queryKey: key1,
|
|
3213
|
-
queryFn: async () => {
|
|
3214
|
-
await sleep(10);
|
|
3215
|
-
return 0;
|
|
3216
|
-
},
|
|
3217
|
-
get select() {
|
|
3218
|
-
const currentCount = count();
|
|
3219
|
-
return (data) => `selected ${data + currentCount}`;
|
|
3220
|
-
},
|
|
3221
|
-
placeholderData: 99,
|
|
3222
|
-
}));
|
|
3223
|
-
return (<div>
|
|
3224
|
-
<h2>Data: {state.data}</h2>
|
|
3225
|
-
<h2>forceValue: {forceValue()}</h2>
|
|
3226
|
-
<button onClick={inc}>inc: {count()}</button>
|
|
3227
|
-
<button onClick={forceUpdate}>forceUpdate</button>
|
|
3228
|
-
</div>);
|
|
3229
|
-
}
|
|
3230
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3231
|
-
<Page />
|
|
3232
|
-
</QueryClientProvider>));
|
|
3233
|
-
await waitFor(() => screen.getByText('Data: selected 101')); // 99 + 2
|
|
3234
|
-
await waitFor(() => screen.getByText('Data: selected 2')); // 0 + 2
|
|
3235
|
-
fireEvent.click(screen.getByRole('button', { name: /inc/i }));
|
|
3236
|
-
await waitFor(() => screen.getByText('Data: selected 3')); // 0 + 3
|
|
3237
|
-
fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i }));
|
|
3238
|
-
await waitFor(() => screen.getByText('forceValue: 2'));
|
|
3239
|
-
// data should still be 3 after an independent re-render
|
|
3240
|
-
await waitFor(() => screen.getByText('Data: selected 3'));
|
|
3241
|
-
});
|
|
3242
|
-
it('select should structurally share data', async () => {
|
|
3243
|
-
const key1 = queryKey();
|
|
3244
|
-
const states = [];
|
|
3245
|
-
function Page() {
|
|
3246
|
-
const [forceValue, setForceValue] = createSignal(1);
|
|
3247
|
-
const state = createQuery(() => ({
|
|
3248
|
-
queryKey: key1,
|
|
3249
|
-
queryFn: async () => {
|
|
3250
|
-
await sleep(10);
|
|
3251
|
-
return [1, 2];
|
|
3252
|
-
},
|
|
3253
|
-
select: (res) => res.map((x) => x + 1),
|
|
3254
|
-
}));
|
|
3255
|
-
createEffect(() => {
|
|
3256
|
-
if (state.data) {
|
|
3257
|
-
states.push(state.data);
|
|
3258
|
-
}
|
|
3259
|
-
});
|
|
3260
|
-
const forceUpdate = () => {
|
|
3261
|
-
setForceValue((prev) => prev + 1);
|
|
3262
|
-
};
|
|
3263
|
-
return (<div>
|
|
3264
|
-
<h2>Data: {JSON.stringify(state.data)}</h2>
|
|
3265
|
-
<h2>forceValue: {forceValue}</h2>
|
|
3266
|
-
<button onClick={forceUpdate}>forceUpdate</button>
|
|
3267
|
-
</div>);
|
|
3268
|
-
}
|
|
3269
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3270
|
-
<Page />
|
|
3271
|
-
</QueryClientProvider>));
|
|
3272
|
-
await waitFor(() => screen.getByText('Data: [2,3]'));
|
|
3273
|
-
expect(states).toHaveLength(1);
|
|
3274
|
-
fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i }));
|
|
3275
|
-
await waitFor(() => screen.getByText('forceValue: 2'));
|
|
3276
|
-
await waitFor(() => screen.getByText('Data: [2,3]'));
|
|
3277
|
-
// effect should not be triggered again due to structural sharing
|
|
3278
|
-
expect(states).toHaveLength(1);
|
|
3279
|
-
});
|
|
3280
|
-
it('The reconcile fn callback should correctly maintain referential equality', async () => {
|
|
3281
|
-
const key1 = queryKey();
|
|
3282
|
-
const states = [];
|
|
3283
|
-
function Page() {
|
|
3284
|
-
const [forceValue, setForceValue] = createSignal(1);
|
|
3285
|
-
const state = createQuery(() => ({
|
|
3286
|
-
queryKey: key1,
|
|
3287
|
-
queryFn: async () => {
|
|
3288
|
-
await sleep(10);
|
|
3289
|
-
return [1, 2];
|
|
3290
|
-
},
|
|
3291
|
-
select: (res) => res.map((x) => x + 1),
|
|
3292
|
-
reconcile(oldData, newData) {
|
|
3293
|
-
return reconcile(newData)(oldData);
|
|
3294
|
-
},
|
|
3295
|
-
}));
|
|
3296
|
-
createEffect(() => {
|
|
3297
|
-
if (state.data) {
|
|
3298
|
-
states.push(state.data);
|
|
3299
|
-
}
|
|
3300
|
-
});
|
|
3301
|
-
const forceUpdate = () => {
|
|
3302
|
-
setForceValue((prev) => prev + 1);
|
|
3303
|
-
};
|
|
3304
|
-
return (<div>
|
|
3305
|
-
<h2>Data: {JSON.stringify(state.data)}</h2>
|
|
3306
|
-
<h2>forceValue: {forceValue}</h2>
|
|
3307
|
-
<button onClick={forceUpdate}>forceUpdate</button>
|
|
3308
|
-
</div>);
|
|
3309
|
-
}
|
|
3310
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3311
|
-
<Page />
|
|
3312
|
-
</QueryClientProvider>));
|
|
3313
|
-
await waitFor(() => screen.getByText('Data: [2,3]'));
|
|
3314
|
-
expect(states).toHaveLength(1);
|
|
3315
|
-
fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i }));
|
|
3316
|
-
await waitFor(() => screen.getByText('forceValue: 2'));
|
|
3317
|
-
await waitFor(() => screen.getByText('Data: [2,3]'));
|
|
3318
|
-
// effect should not be triggered again due to structural sharing
|
|
3319
|
-
expect(states).toHaveLength(1);
|
|
3320
|
-
});
|
|
3321
|
-
it('should cancel the query function when there are no more subscriptions', async () => {
|
|
3322
|
-
const key = queryKey();
|
|
3323
|
-
let cancelFn = vi.fn();
|
|
3324
|
-
const queryFn = ({ signal }) => {
|
|
3325
|
-
const promise = new Promise((resolve, reject) => {
|
|
3326
|
-
cancelFn = vi.fn(() => reject('Cancelled'));
|
|
3327
|
-
signal?.addEventListener('abort', cancelFn);
|
|
3328
|
-
sleep(20).then(() => resolve('OK'));
|
|
3329
|
-
});
|
|
3330
|
-
return promise;
|
|
3331
|
-
};
|
|
3332
|
-
function Page() {
|
|
3333
|
-
const state = createQuery(() => ({ queryKey: key, queryFn }));
|
|
3334
|
-
return (<div>
|
|
3335
|
-
<h1>Status: {state.status}</h1>
|
|
3336
|
-
</div>);
|
|
3337
|
-
}
|
|
3338
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3339
|
-
<Blink duration={5}>
|
|
3340
|
-
<Page />
|
|
3341
|
-
</Blink>
|
|
3342
|
-
</QueryClientProvider>));
|
|
3343
|
-
await waitFor(() => screen.getByText('off'));
|
|
3344
|
-
expect(cancelFn).toHaveBeenCalled();
|
|
3345
|
-
});
|
|
3346
|
-
it('should cancel the query if the signal was consumed and there are no more subscriptions', async () => {
|
|
3347
|
-
const key = queryKey();
|
|
3348
|
-
const states = [];
|
|
3349
|
-
const queryFn = async (ctx) => {
|
|
3350
|
-
const [, limit] = ctx.queryKey;
|
|
3351
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
3352
|
-
const value = limit % 2 && ctx.signal ? 'abort' : `data ${limit}`;
|
|
3353
|
-
await sleep(25);
|
|
3354
|
-
return value;
|
|
3355
|
-
};
|
|
3356
|
-
function Page(props) {
|
|
3357
|
-
const state = createQuery(() => ({
|
|
3358
|
-
queryKey: [key, props.limit],
|
|
3359
|
-
queryFn,
|
|
3360
|
-
}));
|
|
3361
|
-
states[props.limit] = state;
|
|
3362
|
-
return (<div>
|
|
3363
|
-
<h1>Status: {state.status}</h1>
|
|
3364
|
-
<h1>data: {state.data}</h1>
|
|
3365
|
-
</div>);
|
|
3366
|
-
}
|
|
3367
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3368
|
-
<Blink duration={5}>
|
|
3369
|
-
<Page limit={0}/>
|
|
3370
|
-
<Page limit={1}/>
|
|
3371
|
-
<Page limit={2}/>
|
|
3372
|
-
<Page limit={3}/>
|
|
3373
|
-
</Blink>
|
|
3374
|
-
</QueryClientProvider>));
|
|
3375
|
-
await waitFor(() => screen.getByText('off'));
|
|
3376
|
-
await sleep(20);
|
|
3377
|
-
await waitFor(() => expect(states).toHaveLength(4));
|
|
3378
|
-
expect(queryCache.find({ queryKey: [key, 0] })?.state).toMatchObject({
|
|
3379
|
-
data: 'data 0',
|
|
3380
|
-
status: 'success',
|
|
3381
|
-
dataUpdateCount: 1,
|
|
3382
|
-
});
|
|
3383
|
-
expect(queryCache.find({ queryKey: [key, 1] })?.state).toMatchObject({
|
|
3384
|
-
data: undefined,
|
|
3385
|
-
status: 'pending',
|
|
3386
|
-
fetchStatus: 'idle',
|
|
3387
|
-
});
|
|
3388
|
-
expect(queryCache.find({ queryKey: [key, 2] })?.state).toMatchObject({
|
|
3389
|
-
data: 'data 2',
|
|
3390
|
-
status: 'success',
|
|
3391
|
-
dataUpdateCount: 1,
|
|
3392
|
-
});
|
|
3393
|
-
expect(queryCache.find({ queryKey: [key, 3] })?.state).toMatchObject({
|
|
3394
|
-
data: undefined,
|
|
3395
|
-
status: 'pending',
|
|
3396
|
-
fetchStatus: 'idle',
|
|
3397
|
-
});
|
|
3398
|
-
});
|
|
3399
|
-
it('should refetch when quickly switching to a failed query', async () => {
|
|
3400
|
-
const key = queryKey();
|
|
3401
|
-
const states = [];
|
|
3402
|
-
const queryFn = async () => {
|
|
3403
|
-
await sleep(50);
|
|
3404
|
-
return 'OK';
|
|
3405
|
-
};
|
|
3406
|
-
function Page() {
|
|
3407
|
-
const [id, setId] = createSignal(1);
|
|
3408
|
-
const [hasChanged, setHasChanged] = createSignal(false);
|
|
3409
|
-
const state = createQuery(() => ({ queryKey: [key, id()], queryFn }));
|
|
3410
|
-
createRenderEffect(() => {
|
|
3411
|
-
states.push({ ...state });
|
|
3412
|
-
});
|
|
3413
|
-
createEffect(on(hasChanged, () => {
|
|
3414
|
-
setId((prevId) => (prevId === 1 ? 2 : 1));
|
|
3415
|
-
setHasChanged(true);
|
|
3416
|
-
}));
|
|
3417
|
-
return null;
|
|
3418
|
-
}
|
|
3419
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3420
|
-
<Page />
|
|
3421
|
-
</QueryClientProvider>));
|
|
3422
|
-
await sleep(100);
|
|
3423
|
-
expect(states.length).toBe(2);
|
|
3424
|
-
// Load query 1
|
|
3425
|
-
expect(states[0]).toMatchObject({
|
|
3426
|
-
status: 'pending',
|
|
3427
|
-
error: null,
|
|
3428
|
-
});
|
|
3429
|
-
// No rerenders - No state updates
|
|
3430
|
-
// Loaded query 1
|
|
3431
|
-
expect(states[1]).toMatchObject({
|
|
3432
|
-
status: 'success',
|
|
3433
|
-
error: null,
|
|
3434
|
-
});
|
|
3435
|
-
});
|
|
3436
|
-
it('should update query state and refetch when reset with resetQueries', async () => {
|
|
3437
|
-
const key = queryKey();
|
|
3438
|
-
const states = [];
|
|
3439
|
-
let count = 0;
|
|
3440
|
-
function Page() {
|
|
3441
|
-
const state = createQuery(() => ({
|
|
3442
|
-
queryKey: key,
|
|
3443
|
-
queryFn: async () => {
|
|
3444
|
-
await sleep(10);
|
|
3445
|
-
count++;
|
|
3446
|
-
return count;
|
|
3447
|
-
},
|
|
3448
|
-
staleTime: Infinity,
|
|
3449
|
-
}));
|
|
3450
|
-
createRenderEffect(() => {
|
|
3451
|
-
states.push({ ...state });
|
|
3452
|
-
});
|
|
3453
|
-
return (<div>
|
|
3454
|
-
<button onClick={() => queryClient.resetQueries({ queryKey: key })}>
|
|
3455
|
-
reset
|
|
3456
|
-
</button>
|
|
3457
|
-
<div>data: {state.data ?? 'null'}</div>
|
|
3458
|
-
<div>isFetching: {state.isFetching}</div>
|
|
3459
|
-
</div>);
|
|
3460
|
-
}
|
|
3461
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3462
|
-
<Page />
|
|
3463
|
-
</QueryClientProvider>));
|
|
3464
|
-
await waitFor(() => screen.getByText('data: 1'));
|
|
3465
|
-
fireEvent.click(screen.getByRole('button', { name: /reset/i }));
|
|
3466
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
3467
|
-
await waitFor(() => screen.getByText('data: 2'));
|
|
3468
|
-
expect(count).toBe(2);
|
|
3469
|
-
expect(states[0]).toMatchObject({
|
|
3470
|
-
isPending: true,
|
|
3471
|
-
isFetching: true,
|
|
3472
|
-
isSuccess: false,
|
|
3473
|
-
isStale: true,
|
|
3474
|
-
});
|
|
3475
|
-
expect(states[1]).toMatchObject({
|
|
3476
|
-
data: 1,
|
|
3477
|
-
isPending: false,
|
|
3478
|
-
isFetching: false,
|
|
3479
|
-
isSuccess: true,
|
|
3480
|
-
isStale: false,
|
|
3481
|
-
});
|
|
3482
|
-
expect(states[2]).toMatchObject({
|
|
3483
|
-
isPending: true,
|
|
3484
|
-
isFetching: true,
|
|
3485
|
-
isSuccess: false,
|
|
3486
|
-
isStale: true,
|
|
3487
|
-
});
|
|
3488
|
-
expect(states[3]).toMatchObject({
|
|
3489
|
-
data: 2,
|
|
3490
|
-
isPending: false,
|
|
3491
|
-
isFetching: false,
|
|
3492
|
-
isSuccess: true,
|
|
3493
|
-
isStale: false,
|
|
3494
|
-
});
|
|
3495
|
-
});
|
|
3496
|
-
it('should update query state and not refetch when resetting a disabled query with resetQueries', async () => {
|
|
3497
|
-
const key = queryKey();
|
|
3498
|
-
const states = [];
|
|
3499
|
-
let count = 0;
|
|
3500
|
-
function Page() {
|
|
3501
|
-
const state = createQuery(() => ({
|
|
3502
|
-
queryKey: key,
|
|
3503
|
-
queryFn: async () => {
|
|
3504
|
-
await sleep(10);
|
|
3505
|
-
count++;
|
|
3506
|
-
return count;
|
|
3507
|
-
},
|
|
3508
|
-
staleTime: Infinity,
|
|
3509
|
-
enabled: false,
|
|
3510
|
-
notifyOnChangeProps: 'all',
|
|
3511
|
-
}));
|
|
3512
|
-
createRenderEffect(() => {
|
|
3513
|
-
states.push({ ...state });
|
|
3514
|
-
});
|
|
3515
|
-
const { refetch } = state;
|
|
3516
|
-
return (<div>
|
|
3517
|
-
<button onClick={() => refetch()}>refetch</button>
|
|
3518
|
-
<button onClick={() => queryClient.resetQueries({ queryKey: key })}>
|
|
3519
|
-
reset
|
|
3520
|
-
</button>
|
|
3521
|
-
<div>data: {state.data ?? 'null'}</div>
|
|
3522
|
-
</div>);
|
|
3523
|
-
}
|
|
3524
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3525
|
-
<Page />
|
|
3526
|
-
</QueryClientProvider>));
|
|
3527
|
-
await waitFor(() => screen.getByText('data: null'));
|
|
3528
|
-
fireEvent.click(screen.getByRole('button', { name: /refetch/i }));
|
|
3529
|
-
await waitFor(() => screen.getByText('data: 1'));
|
|
3530
|
-
fireEvent.click(screen.getByRole('button', { name: /reset/i }));
|
|
3531
|
-
await waitFor(() => screen.getByText('data: null'));
|
|
3532
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
3533
|
-
expect(count).toBe(1);
|
|
3534
|
-
expect(states[0]).toMatchObject({
|
|
3535
|
-
isPending: true,
|
|
3536
|
-
isFetching: false,
|
|
3537
|
-
isSuccess: false,
|
|
3538
|
-
isStale: true,
|
|
3539
|
-
});
|
|
3540
|
-
expect(states[1]).toMatchObject({
|
|
3541
|
-
isPending: true,
|
|
3542
|
-
isFetching: true,
|
|
3543
|
-
isSuccess: false,
|
|
3544
|
-
isStale: true,
|
|
3545
|
-
});
|
|
3546
|
-
expect(states[2]).toMatchObject({
|
|
3547
|
-
data: 1,
|
|
3548
|
-
isPending: false,
|
|
3549
|
-
isFetching: false,
|
|
3550
|
-
isSuccess: true,
|
|
3551
|
-
isStale: false,
|
|
3552
|
-
});
|
|
3553
|
-
expect(states[3]).toMatchObject({
|
|
3554
|
-
isPending: true,
|
|
3555
|
-
isFetching: false,
|
|
3556
|
-
isSuccess: false,
|
|
3557
|
-
isStale: true,
|
|
3558
|
-
});
|
|
3559
|
-
});
|
|
3560
|
-
it('should only call the query hash function once each render', async () => {
|
|
3561
|
-
const key = queryKey();
|
|
3562
|
-
let hashes = 0;
|
|
3563
|
-
let renders = 0;
|
|
3564
|
-
function queryKeyHashFn(x) {
|
|
3565
|
-
hashes++;
|
|
3566
|
-
return JSON.stringify(x);
|
|
3567
|
-
}
|
|
3568
|
-
function Page() {
|
|
3569
|
-
const state = createQuery(() => ({
|
|
3570
|
-
queryKey: key,
|
|
3571
|
-
queryFn: () => 'test',
|
|
3572
|
-
queryKeyHashFn,
|
|
3573
|
-
}));
|
|
3574
|
-
createEffect(on(() => state.status, () => {
|
|
3575
|
-
renders++;
|
|
3576
|
-
}));
|
|
3577
|
-
return null;
|
|
3578
|
-
}
|
|
3579
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3580
|
-
<Page />
|
|
3581
|
-
</QueryClientProvider>));
|
|
3582
|
-
await sleep(10);
|
|
3583
|
-
expect(renders).toBe(hashes);
|
|
3584
|
-
});
|
|
3585
|
-
it('should refetch when changed enabled to true in error state', async () => {
|
|
3586
|
-
const queryFn = vi.fn();
|
|
3587
|
-
queryFn.mockImplementation(async () => {
|
|
3588
|
-
await sleep(10);
|
|
3589
|
-
return Promise.reject(new Error('Suspense Error Bingo'));
|
|
3590
|
-
});
|
|
3591
|
-
function Page(props) {
|
|
3592
|
-
const state = createQuery(() => ({
|
|
3593
|
-
queryKey: ['key'],
|
|
3594
|
-
queryFn,
|
|
3595
|
-
enabled: props.enabled,
|
|
3596
|
-
retry: false,
|
|
3597
|
-
retryOnMount: false,
|
|
3598
|
-
refetchOnMount: false,
|
|
3599
|
-
refetchOnWindowFocus: false,
|
|
3600
|
-
}));
|
|
3601
|
-
return (<Switch fallback={<div>rendered</div>}>
|
|
3602
|
-
<Match when={state.isPending}>
|
|
3603
|
-
<div>status: pending</div>
|
|
3604
|
-
</Match>
|
|
3605
|
-
<Match when={state.error instanceof Error}>
|
|
3606
|
-
<div>error</div>
|
|
3607
|
-
</Match>
|
|
3608
|
-
</Switch>);
|
|
3609
|
-
}
|
|
3610
|
-
function App() {
|
|
3611
|
-
const [enabled, setEnabled] = createSignal(true);
|
|
3612
|
-
const toggle = () => setEnabled((prev) => !prev);
|
|
3613
|
-
return (<div>
|
|
3614
|
-
<Page enabled={enabled()}/>
|
|
3615
|
-
<button aria-label="retry" onClick={toggle}>
|
|
3616
|
-
retry {enabled()}
|
|
3617
|
-
</button>
|
|
3618
|
-
</div>);
|
|
3619
|
-
}
|
|
3620
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3621
|
-
<App />
|
|
3622
|
-
</QueryClientProvider>));
|
|
3623
|
-
// initial state check
|
|
3624
|
-
screen.getByText('status: pending');
|
|
3625
|
-
// // render error state component
|
|
3626
|
-
await waitFor(() => screen.getByText('error'));
|
|
3627
|
-
expect(queryFn).toBeCalledTimes(1);
|
|
3628
|
-
// change to enabled to false
|
|
3629
|
-
fireEvent.click(screen.getByLabelText('retry'));
|
|
3630
|
-
await waitFor(() => screen.getByText('error'));
|
|
3631
|
-
expect(queryFn).toBeCalledTimes(1);
|
|
3632
|
-
// // change to enabled to true
|
|
3633
|
-
fireEvent.click(screen.getByLabelText('retry'));
|
|
3634
|
-
expect(queryFn).toBeCalledTimes(2);
|
|
3635
|
-
});
|
|
3636
|
-
it('should refetch when query key changed when previous status is error', async () => {
|
|
3637
|
-
function Page(props) {
|
|
3638
|
-
const state = createQuery(() => ({
|
|
3639
|
-
queryKey: [props.id],
|
|
3640
|
-
queryFn: async () => {
|
|
3641
|
-
await sleep(10);
|
|
3642
|
-
if (props.id % 2 === 1) {
|
|
3643
|
-
return Promise.reject(new Error('Error'));
|
|
3644
|
-
}
|
|
3645
|
-
else {
|
|
3646
|
-
return 'data';
|
|
3647
|
-
}
|
|
3648
|
-
},
|
|
3649
|
-
retry: false,
|
|
3650
|
-
retryOnMount: false,
|
|
3651
|
-
refetchOnMount: false,
|
|
3652
|
-
refetchOnWindowFocus: false,
|
|
3653
|
-
}));
|
|
3654
|
-
return (<Switch fallback={<div>rendered</div>}>
|
|
3655
|
-
<Match when={state.isPending}>
|
|
3656
|
-
<div>status: pending</div>
|
|
3657
|
-
</Match>
|
|
3658
|
-
<Match when={state.error instanceof Error}>
|
|
3659
|
-
<div>error</div>
|
|
3660
|
-
</Match>
|
|
3661
|
-
</Switch>);
|
|
3662
|
-
}
|
|
3663
|
-
function App() {
|
|
3664
|
-
const [id, setId] = createSignal(1);
|
|
3665
|
-
const changeId = () => setId((x) => x + 1);
|
|
3666
|
-
return (<div>
|
|
3667
|
-
<Page id={id()}/>
|
|
3668
|
-
<button aria-label="change" onClick={changeId}>
|
|
3669
|
-
change {id()}
|
|
3670
|
-
</button>
|
|
3671
|
-
</div>);
|
|
3672
|
-
}
|
|
3673
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3674
|
-
<App />
|
|
3675
|
-
</QueryClientProvider>));
|
|
3676
|
-
// initial state check
|
|
3677
|
-
screen.getByText('status: pending');
|
|
3678
|
-
// render error state component
|
|
3679
|
-
await waitFor(() => screen.getByText('error'));
|
|
3680
|
-
// change to unmount query
|
|
3681
|
-
fireEvent.click(screen.getByLabelText('change'));
|
|
3682
|
-
await waitFor(() => screen.getByText('rendered'));
|
|
3683
|
-
// change to mount new query
|
|
3684
|
-
fireEvent.click(screen.getByLabelText('change'));
|
|
3685
|
-
await waitFor(() => screen.getByText('error'));
|
|
3686
|
-
});
|
|
3687
|
-
it('should refetch when query key changed when switching between erroneous queries', async () => {
|
|
3688
|
-
function Page(props) {
|
|
3689
|
-
const state = createQuery(() => ({
|
|
3690
|
-
queryKey: [props.id],
|
|
3691
|
-
queryFn: async () => {
|
|
3692
|
-
await sleep(10);
|
|
3693
|
-
return Promise.reject(new Error('Error'));
|
|
3694
|
-
},
|
|
3695
|
-
retry: false,
|
|
3696
|
-
retryOnMount: false,
|
|
3697
|
-
refetchOnMount: false,
|
|
3698
|
-
refetchOnWindowFocus: false,
|
|
3699
|
-
}));
|
|
3700
|
-
return (<Switch fallback={<div>rendered</div>}>
|
|
3701
|
-
<Match when={state.isFetching}>
|
|
3702
|
-
<div>status: fetching</div>
|
|
3703
|
-
</Match>
|
|
3704
|
-
<Match when={state.error instanceof Error}>
|
|
3705
|
-
<div>error</div>
|
|
3706
|
-
</Match>
|
|
3707
|
-
</Switch>);
|
|
3708
|
-
}
|
|
3709
|
-
function App() {
|
|
3710
|
-
const [value, setValue] = createSignal(true);
|
|
3711
|
-
const toggle = () => setValue((x) => !x);
|
|
3712
|
-
return (<div>
|
|
3713
|
-
<Page id={value()}/>
|
|
3714
|
-
<button aria-label="change" onClick={toggle}>
|
|
3715
|
-
change {value()}
|
|
3716
|
-
</button>
|
|
3717
|
-
</div>);
|
|
3718
|
-
}
|
|
3719
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3720
|
-
<App />
|
|
3721
|
-
</QueryClientProvider>));
|
|
3722
|
-
// initial state check
|
|
3723
|
-
screen.getByText('status: fetching');
|
|
3724
|
-
// render error state component
|
|
3725
|
-
await waitFor(() => screen.getByText('error'));
|
|
3726
|
-
// change to mount second query
|
|
3727
|
-
fireEvent.click(screen.getByLabelText('change'));
|
|
3728
|
-
await waitFor(() => screen.getByText('status: fetching'));
|
|
3729
|
-
await waitFor(() => screen.getByText('error'));
|
|
3730
|
-
// change to mount first query again
|
|
3731
|
-
fireEvent.click(screen.getByLabelText('change'));
|
|
3732
|
-
await waitFor(() => screen.getByText('status: fetching'));
|
|
3733
|
-
await waitFor(() => screen.getByText('error'));
|
|
3734
|
-
});
|
|
3735
|
-
it('should have no error in pending state when refetching after error occurred', async () => {
|
|
3736
|
-
const key = queryKey();
|
|
3737
|
-
const states = [];
|
|
3738
|
-
const error = new Error('oops');
|
|
3739
|
-
let count = 0;
|
|
3740
|
-
function Page() {
|
|
3741
|
-
const state = createQuery(() => ({
|
|
3742
|
-
queryKey: key,
|
|
3743
|
-
queryFn: async () => {
|
|
3744
|
-
await sleep(10);
|
|
3745
|
-
if (count === 0) {
|
|
3746
|
-
count++;
|
|
3747
|
-
throw error;
|
|
3748
|
-
}
|
|
3749
|
-
return 5;
|
|
3750
|
-
},
|
|
3751
|
-
retry: false,
|
|
3752
|
-
}));
|
|
3753
|
-
createRenderEffect(() => {
|
|
3754
|
-
states.push({ ...state });
|
|
3755
|
-
});
|
|
3756
|
-
return (<Switch fallback={<div>data: {state.data}</div>}>
|
|
3757
|
-
<Match when={state.isPending}>
|
|
3758
|
-
<div>status: pending</div>
|
|
3759
|
-
</Match>
|
|
3760
|
-
<Match when={state.error instanceof Error}>
|
|
3761
|
-
<div>
|
|
3762
|
-
<div>error</div>
|
|
3763
|
-
<button onClick={() => state.refetch()}>refetch</button>
|
|
3764
|
-
</div>
|
|
3765
|
-
</Match>
|
|
3766
|
-
</Switch>);
|
|
3767
|
-
}
|
|
3768
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3769
|
-
<Page />
|
|
3770
|
-
</QueryClientProvider>));
|
|
3771
|
-
await waitFor(() => screen.getByText('error'));
|
|
3772
|
-
fireEvent.click(screen.getByRole('button', { name: 'refetch' }));
|
|
3773
|
-
await waitFor(() => screen.getByText('data: 5'));
|
|
3774
|
-
await waitFor(() => expect(states.length).toBe(4));
|
|
3775
|
-
expect(states[0]).toMatchObject({
|
|
3776
|
-
status: 'pending',
|
|
3777
|
-
data: undefined,
|
|
3778
|
-
error: null,
|
|
3779
|
-
});
|
|
3780
|
-
expect(states[1]).toMatchObject({
|
|
3781
|
-
status: 'error',
|
|
3782
|
-
data: undefined,
|
|
3783
|
-
error,
|
|
3784
|
-
});
|
|
3785
|
-
expect(states[2]).toMatchObject({
|
|
3786
|
-
status: 'pending',
|
|
3787
|
-
data: undefined,
|
|
3788
|
-
error: null,
|
|
3789
|
-
});
|
|
3790
|
-
expect(states[3]).toMatchObject({
|
|
3791
|
-
status: 'success',
|
|
3792
|
-
data: 5,
|
|
3793
|
-
error: null,
|
|
3794
|
-
});
|
|
3795
|
-
});
|
|
3796
|
-
describe('networkMode online', () => {
|
|
3797
|
-
it('online queries should not start fetching if you are offline', async () => {
|
|
3798
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
3799
|
-
const key = queryKey();
|
|
3800
|
-
const states = [];
|
|
3801
|
-
function Page() {
|
|
3802
|
-
const state = createQuery(() => ({
|
|
3803
|
-
queryKey: key,
|
|
3804
|
-
queryFn: async () => {
|
|
3805
|
-
await sleep(10);
|
|
3806
|
-
return 'data';
|
|
3807
|
-
},
|
|
3808
|
-
}));
|
|
3809
|
-
createEffect(() => {
|
|
3810
|
-
states.push(state.fetchStatus);
|
|
3811
|
-
});
|
|
3812
|
-
return (<div>
|
|
3813
|
-
<div>
|
|
3814
|
-
status: {state.status}, isPaused: {String(state.isPaused)}
|
|
3815
|
-
</div>
|
|
3816
|
-
<div>data: {state.data}</div>
|
|
3817
|
-
</div>);
|
|
3818
|
-
}
|
|
3819
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3820
|
-
<Page />
|
|
3821
|
-
</QueryClientProvider>));
|
|
3822
|
-
await waitFor(() => screen.getByText('status: pending, isPaused: true'));
|
|
3823
|
-
onlineMock.mockReturnValue(true);
|
|
3824
|
-
window.dispatchEvent(new Event('online'));
|
|
3825
|
-
await waitFor(() => screen.getByText('status: success, isPaused: false'));
|
|
3826
|
-
await waitFor(() => {
|
|
3827
|
-
expect(screen.getByText('data: data')).toBeInTheDocument();
|
|
3828
|
-
});
|
|
3829
|
-
expect(states).toEqual(['paused', 'fetching', 'idle']);
|
|
3830
|
-
onlineMock.mockRestore();
|
|
3831
|
-
});
|
|
3832
|
-
it('online queries should not refetch if you are offline', async () => {
|
|
3833
|
-
const key = queryKey();
|
|
3834
|
-
let count = 0;
|
|
3835
|
-
function Page() {
|
|
3836
|
-
const state = createQuery(() => ({
|
|
3837
|
-
queryKey: key,
|
|
3838
|
-
queryFn: async () => {
|
|
3839
|
-
count++;
|
|
3840
|
-
await sleep(10);
|
|
3841
|
-
return 'data' + count;
|
|
3842
|
-
},
|
|
3843
|
-
}));
|
|
3844
|
-
return (<div>
|
|
3845
|
-
<div>
|
|
3846
|
-
status: {state.status}, fetchStatus: {state.fetchStatus},
|
|
3847
|
-
failureCount: {state.failureCount}
|
|
3848
|
-
</div>
|
|
3849
|
-
<div>failureReason: {state.failureReason ?? 'null'}</div>
|
|
3850
|
-
<div>data: {state.data}</div>
|
|
3851
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
3852
|
-
invalidate
|
|
3853
|
-
</button>
|
|
3854
|
-
</div>);
|
|
3855
|
-
}
|
|
3856
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3857
|
-
<Page />
|
|
3858
|
-
</QueryClientProvider>));
|
|
3859
|
-
await waitFor(() => screen.getByText('data: data1'));
|
|
3860
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
3861
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
3862
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused, failureCount: 0'));
|
|
3863
|
-
await waitFor(() => screen.getByText('failureReason: null'));
|
|
3864
|
-
onlineMock.mockReturnValue(true);
|
|
3865
|
-
window.dispatchEvent(new Event('online'));
|
|
3866
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: fetching, failureCount: 0'));
|
|
3867
|
-
await waitFor(() => screen.getByText('failureReason: null'));
|
|
3868
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: idle, failureCount: 0'));
|
|
3869
|
-
await waitFor(() => screen.getByText('failureReason: null'));
|
|
3870
|
-
await waitFor(() => {
|
|
3871
|
-
expect(screen.getByText('data: data2')).toBeInTheDocument();
|
|
3872
|
-
});
|
|
3873
|
-
onlineMock.mockRestore();
|
|
3874
|
-
});
|
|
3875
|
-
it('online queries should not refetch if you are offline and refocus', async () => {
|
|
3876
|
-
const key = queryKey();
|
|
3877
|
-
let count = 0;
|
|
3878
|
-
function Page() {
|
|
3879
|
-
const state = createQuery(() => ({
|
|
3880
|
-
queryKey: key,
|
|
3881
|
-
queryFn: async () => {
|
|
3882
|
-
count++;
|
|
3883
|
-
await sleep(10);
|
|
3884
|
-
return 'data' + count;
|
|
3885
|
-
},
|
|
3886
|
-
}));
|
|
3887
|
-
return (<div>
|
|
3888
|
-
<div>
|
|
3889
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
3890
|
-
</div>
|
|
3891
|
-
<div>data: {state.data}</div>
|
|
3892
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
3893
|
-
invalidate
|
|
3894
|
-
</button>
|
|
3895
|
-
</div>);
|
|
3896
|
-
}
|
|
3897
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3898
|
-
<Page />
|
|
3899
|
-
</QueryClientProvider>));
|
|
3900
|
-
await waitFor(() => screen.getByText('data: data1'));
|
|
3901
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
3902
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
3903
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused'));
|
|
3904
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
3905
|
-
await sleep(15);
|
|
3906
|
-
await waitFor(() => expect(screen.queryByText('data: data2')).not.toBeInTheDocument());
|
|
3907
|
-
expect(count).toBe(1);
|
|
3908
|
-
onlineMock.mockRestore();
|
|
3909
|
-
});
|
|
3910
|
-
it('online queries should not refetch while already paused', async () => {
|
|
3911
|
-
const key = queryKey();
|
|
3912
|
-
let count = 0;
|
|
3913
|
-
function Page() {
|
|
3914
|
-
const state = createQuery(() => ({
|
|
3915
|
-
queryKey: key,
|
|
3916
|
-
queryFn: async () => {
|
|
3917
|
-
count++;
|
|
3918
|
-
await sleep(10);
|
|
3919
|
-
return 'data' + count;
|
|
3920
|
-
},
|
|
3921
|
-
}));
|
|
3922
|
-
return (<div>
|
|
3923
|
-
<div>
|
|
3924
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
3925
|
-
</div>
|
|
3926
|
-
<div>data: {state.data}</div>
|
|
3927
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
3928
|
-
invalidate
|
|
3929
|
-
</button>
|
|
3930
|
-
</div>);
|
|
3931
|
-
}
|
|
3932
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
3933
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3934
|
-
<Page />
|
|
3935
|
-
</QueryClientProvider>));
|
|
3936
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: paused'));
|
|
3937
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
3938
|
-
await sleep(15);
|
|
3939
|
-
// invalidation should not trigger a refetch
|
|
3940
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: paused'));
|
|
3941
|
-
expect(count).toBe(0);
|
|
3942
|
-
onlineMock.mockRestore();
|
|
3943
|
-
});
|
|
3944
|
-
it('online queries should not refetch while already paused if data is in the cache', async () => {
|
|
3945
|
-
const key = queryKey();
|
|
3946
|
-
let count = 0;
|
|
3947
|
-
function Page() {
|
|
3948
|
-
const state = createQuery(() => ({
|
|
3949
|
-
queryKey: key,
|
|
3950
|
-
queryFn: async () => {
|
|
3951
|
-
count++;
|
|
3952
|
-
await sleep(10);
|
|
3953
|
-
return 'data' + count;
|
|
3954
|
-
},
|
|
3955
|
-
initialData: 'initial',
|
|
3956
|
-
}));
|
|
3957
|
-
return (<div>
|
|
3958
|
-
<div>
|
|
3959
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
3960
|
-
</div>
|
|
3961
|
-
<div>data: {state.data}</div>
|
|
3962
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
3963
|
-
invalidate
|
|
3964
|
-
</button>
|
|
3965
|
-
</div>);
|
|
3966
|
-
}
|
|
3967
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
3968
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
3969
|
-
<Page />
|
|
3970
|
-
</QueryClientProvider>));
|
|
3971
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused'));
|
|
3972
|
-
await waitFor(() => {
|
|
3973
|
-
expect(screen.getByText('data: initial')).toBeInTheDocument();
|
|
3974
|
-
});
|
|
3975
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
3976
|
-
await sleep(15);
|
|
3977
|
-
// invalidation should not trigger a refetch
|
|
3978
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused'));
|
|
3979
|
-
expect(count).toBe(0);
|
|
3980
|
-
onlineMock.mockRestore();
|
|
3981
|
-
});
|
|
3982
|
-
it('online queries should not get stuck in fetching state when pausing multiple times', async () => {
|
|
3983
|
-
const key = queryKey();
|
|
3984
|
-
let count = 0;
|
|
3985
|
-
function Page() {
|
|
3986
|
-
const state = createQuery(() => ({
|
|
3987
|
-
queryKey: key,
|
|
3988
|
-
queryFn: async () => {
|
|
3989
|
-
count++;
|
|
3990
|
-
await sleep(10);
|
|
3991
|
-
return 'data' + count;
|
|
3992
|
-
},
|
|
3993
|
-
initialData: 'initial',
|
|
3994
|
-
}));
|
|
3995
|
-
return (<div>
|
|
3996
|
-
<div>
|
|
3997
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
3998
|
-
</div>
|
|
3999
|
-
<div>data: {state.data}</div>
|
|
4000
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
4001
|
-
invalidate
|
|
4002
|
-
</button>
|
|
4003
|
-
</div>);
|
|
4004
|
-
}
|
|
4005
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4006
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4007
|
-
<Page />
|
|
4008
|
-
</QueryClientProvider>));
|
|
4009
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused'));
|
|
4010
|
-
await waitFor(() => {
|
|
4011
|
-
expect(screen.getByText('data: initial')).toBeInTheDocument();
|
|
4012
|
-
});
|
|
4013
|
-
// triggers one pause
|
|
4014
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
4015
|
-
await sleep(15);
|
|
4016
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused'));
|
|
4017
|
-
// triggers a second pause
|
|
4018
|
-
window.dispatchEvent(new Event('visibilitychange'));
|
|
4019
|
-
onlineMock.mockReturnValue(true);
|
|
4020
|
-
window.dispatchEvent(new Event('online'));
|
|
4021
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: idle'));
|
|
4022
|
-
await waitFor(() => {
|
|
4023
|
-
expect(screen.getByText('data: data1')).toBeInTheDocument();
|
|
4024
|
-
});
|
|
4025
|
-
expect(count).toBe(1);
|
|
4026
|
-
onlineMock.mockRestore();
|
|
4027
|
-
});
|
|
4028
|
-
it('online queries should pause retries if you are offline', async () => {
|
|
4029
|
-
const key = queryKey();
|
|
4030
|
-
let count = 0;
|
|
4031
|
-
function Page() {
|
|
4032
|
-
const state = createQuery(() => ({
|
|
4033
|
-
queryKey: key,
|
|
4034
|
-
queryFn: async () => {
|
|
4035
|
-
count++;
|
|
4036
|
-
await sleep(10);
|
|
4037
|
-
throw new Error('failed' + count);
|
|
4038
|
-
},
|
|
4039
|
-
retry: 2,
|
|
4040
|
-
retryDelay: 10,
|
|
4041
|
-
}));
|
|
4042
|
-
return (<div>
|
|
4043
|
-
<div>
|
|
4044
|
-
status: {state.status}, fetchStatus: {state.fetchStatus},
|
|
4045
|
-
failureCount: {state.failureCount}
|
|
4046
|
-
</div>
|
|
4047
|
-
<div>failureReason: {state.failureReason?.message ?? 'null'}</div>
|
|
4048
|
-
</div>);
|
|
4049
|
-
}
|
|
4050
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4051
|
-
<Page />
|
|
4052
|
-
</QueryClientProvider>));
|
|
4053
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: fetching, failureCount: 1'));
|
|
4054
|
-
await waitFor(() => screen.getByText('failureReason: failed1'));
|
|
4055
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4056
|
-
await sleep(20);
|
|
4057
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: paused, failureCount: 1'));
|
|
4058
|
-
await waitFor(() => screen.getByText('failureReason: failed1'));
|
|
4059
|
-
expect(count).toBe(1);
|
|
4060
|
-
onlineMock.mockReturnValue(true);
|
|
4061
|
-
window.dispatchEvent(new Event('online'));
|
|
4062
|
-
await waitFor(() => screen.getByText('status: error, fetchStatus: idle, failureCount: 3'));
|
|
4063
|
-
await waitFor(() => screen.getByText('failureReason: failed3'));
|
|
4064
|
-
expect(count).toBe(3);
|
|
4065
|
-
onlineMock.mockRestore();
|
|
4066
|
-
});
|
|
4067
|
-
it('online queries should fetch if paused and we go online even if already unmounted (because not cancelled)', async () => {
|
|
4068
|
-
const key = queryKey();
|
|
4069
|
-
let count = 0;
|
|
4070
|
-
function Component() {
|
|
4071
|
-
const state = createQuery(() => ({
|
|
4072
|
-
queryKey: key,
|
|
4073
|
-
queryFn: async () => {
|
|
4074
|
-
count++;
|
|
4075
|
-
await sleep(10);
|
|
4076
|
-
return 'data' + count;
|
|
4077
|
-
},
|
|
4078
|
-
}));
|
|
4079
|
-
return (<div>
|
|
4080
|
-
<div>
|
|
4081
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
4082
|
-
</div>
|
|
4083
|
-
<div>data: {state.data}</div>
|
|
4084
|
-
</div>);
|
|
4085
|
-
}
|
|
4086
|
-
function Page() {
|
|
4087
|
-
const [show, setShow] = createSignal(true);
|
|
4088
|
-
return (<div>
|
|
4089
|
-
{show() && <Component />}
|
|
4090
|
-
<button onClick={() => setShow(false)}>hide</button>
|
|
4091
|
-
</div>);
|
|
4092
|
-
}
|
|
4093
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4094
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4095
|
-
<Page />
|
|
4096
|
-
</QueryClientProvider>));
|
|
4097
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: paused'));
|
|
4098
|
-
fireEvent.click(screen.getByRole('button', { name: /hide/i }));
|
|
4099
|
-
onlineMock.mockReturnValue(true);
|
|
4100
|
-
window.dispatchEvent(new Event('online'));
|
|
4101
|
-
await sleep(15);
|
|
4102
|
-
expect(queryClient.getQueryState(key)).toMatchObject({
|
|
4103
|
-
fetchStatus: 'idle',
|
|
4104
|
-
status: 'success',
|
|
4105
|
-
});
|
|
4106
|
-
expect(count).toBe(1);
|
|
4107
|
-
onlineMock.mockRestore();
|
|
4108
|
-
});
|
|
4109
|
-
it('online queries should not fetch if paused and we go online when cancelled and no refetchOnReconnect', async () => {
|
|
4110
|
-
const key = queryKey();
|
|
4111
|
-
let count = 0;
|
|
4112
|
-
function Page() {
|
|
4113
|
-
const state = createQuery(() => ({
|
|
4114
|
-
queryKey: key,
|
|
4115
|
-
queryFn: async () => {
|
|
4116
|
-
count++;
|
|
4117
|
-
await sleep(10);
|
|
4118
|
-
return 'data' + count;
|
|
4119
|
-
},
|
|
4120
|
-
refetchOnReconnect: false,
|
|
4121
|
-
}));
|
|
4122
|
-
return (<div>
|
|
4123
|
-
<button onClick={() => queryClient.cancelQueries({ queryKey: key })}>
|
|
4124
|
-
cancel
|
|
4125
|
-
</button>
|
|
4126
|
-
<div>
|
|
4127
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
4128
|
-
</div>
|
|
4129
|
-
<div>data: {state.data}</div>
|
|
4130
|
-
</div>);
|
|
4131
|
-
}
|
|
4132
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4133
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4134
|
-
<Page />
|
|
4135
|
-
</QueryClientProvider>));
|
|
4136
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: paused'));
|
|
4137
|
-
fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
|
|
4138
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: idle'));
|
|
4139
|
-
expect(count).toBe(0);
|
|
4140
|
-
onlineMock.mockReturnValue(true);
|
|
4141
|
-
window.dispatchEvent(new Event('online'));
|
|
4142
|
-
await sleep(15);
|
|
4143
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: idle'));
|
|
4144
|
-
expect(count).toBe(0);
|
|
4145
|
-
onlineMock.mockRestore();
|
|
4146
|
-
});
|
|
4147
|
-
it('online queries should not fetch if paused and we go online if already unmounted when signal consumed', async () => {
|
|
4148
|
-
const key = queryKey();
|
|
4149
|
-
let count = 0;
|
|
4150
|
-
function Component() {
|
|
4151
|
-
const state = createQuery(() => ({
|
|
4152
|
-
queryKey: key,
|
|
4153
|
-
queryFn: async ({ signal: _signal }) => {
|
|
4154
|
-
count++;
|
|
4155
|
-
await sleep(10);
|
|
4156
|
-
return `signal${count}`;
|
|
4157
|
-
},
|
|
4158
|
-
}));
|
|
4159
|
-
return (<div>
|
|
4160
|
-
<div>
|
|
4161
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
4162
|
-
</div>
|
|
4163
|
-
<div>data: {state.data}</div>
|
|
4164
|
-
</div>);
|
|
4165
|
-
}
|
|
4166
|
-
function Page() {
|
|
4167
|
-
const [show, setShow] = createSignal(true);
|
|
4168
|
-
return (<div>
|
|
4169
|
-
{show() && <Component />}
|
|
4170
|
-
<button onClick={() => setShow(false)}>hide</button>
|
|
4171
|
-
<button onClick={() => queryClient.invalidateQueries({ queryKey: key })}>
|
|
4172
|
-
invalidate
|
|
4173
|
-
</button>
|
|
4174
|
-
</div>);
|
|
4175
|
-
}
|
|
4176
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4177
|
-
<Page />
|
|
4178
|
-
</QueryClientProvider>));
|
|
4179
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: idle'));
|
|
4180
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4181
|
-
fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
|
|
4182
|
-
await waitFor(() => screen.getByText('status: success, fetchStatus: paused'));
|
|
4183
|
-
fireEvent.click(screen.getByRole('button', { name: /hide/i }));
|
|
4184
|
-
await sleep(15);
|
|
4185
|
-
onlineMock.mockReturnValue(true);
|
|
4186
|
-
window.dispatchEvent(new Event('online'));
|
|
4187
|
-
await sleep(15);
|
|
4188
|
-
expect(queryClient.getQueryState(key)).toMatchObject({
|
|
4189
|
-
fetchStatus: 'idle',
|
|
4190
|
-
status: 'success',
|
|
4191
|
-
});
|
|
4192
|
-
expect(count).toBe(1);
|
|
4193
|
-
onlineMock.mockRestore();
|
|
4194
|
-
});
|
|
4195
|
-
});
|
|
4196
|
-
describe('networkMode always', () => {
|
|
4197
|
-
it('always queries should start fetching even if you are offline', async () => {
|
|
4198
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4199
|
-
const key = queryKey();
|
|
4200
|
-
let count = 0;
|
|
4201
|
-
function Page() {
|
|
4202
|
-
const state = createQuery(() => ({
|
|
4203
|
-
queryKey: key,
|
|
4204
|
-
queryFn: async () => {
|
|
4205
|
-
count++;
|
|
4206
|
-
await sleep(10);
|
|
4207
|
-
return 'data ' + count;
|
|
4208
|
-
},
|
|
4209
|
-
networkMode: 'always',
|
|
4210
|
-
}));
|
|
4211
|
-
return (<div>
|
|
4212
|
-
<div>
|
|
4213
|
-
status: {state.status}, isPaused: {String(state.isPaused)}
|
|
4214
|
-
</div>
|
|
4215
|
-
<div>data: {state.data}</div>
|
|
4216
|
-
</div>);
|
|
4217
|
-
}
|
|
4218
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4219
|
-
<Page />
|
|
4220
|
-
</QueryClientProvider>));
|
|
4221
|
-
await waitFor(() => screen.getByText('status: success, isPaused: false'));
|
|
4222
|
-
await waitFor(() => {
|
|
4223
|
-
expect(screen.getByText('data: data 1')).toBeInTheDocument();
|
|
4224
|
-
});
|
|
4225
|
-
onlineMock.mockRestore();
|
|
4226
|
-
});
|
|
4227
|
-
it('always queries should not pause retries', async () => {
|
|
4228
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4229
|
-
const key = queryKey();
|
|
4230
|
-
let count = 0;
|
|
4231
|
-
function Page() {
|
|
4232
|
-
const state = createQuery(() => ({
|
|
4233
|
-
queryKey: key,
|
|
4234
|
-
queryFn: async () => {
|
|
4235
|
-
count++;
|
|
4236
|
-
await sleep(10);
|
|
4237
|
-
throw new Error('error ' + count);
|
|
4238
|
-
},
|
|
4239
|
-
networkMode: 'always',
|
|
4240
|
-
retry: 1,
|
|
4241
|
-
retryDelay: 5,
|
|
4242
|
-
}));
|
|
4243
|
-
return (<div>
|
|
4244
|
-
<div>
|
|
4245
|
-
status: {state.status}, isPaused: {String(state.isPaused)}
|
|
4246
|
-
</div>
|
|
4247
|
-
<div>
|
|
4248
|
-
error: {state.error instanceof Error && state.error.message}
|
|
4249
|
-
</div>
|
|
4250
|
-
</div>);
|
|
4251
|
-
}
|
|
4252
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4253
|
-
<Page />
|
|
4254
|
-
</QueryClientProvider>));
|
|
4255
|
-
await waitFor(() => screen.getByText('status: error, isPaused: false'));
|
|
4256
|
-
await waitFor(() => {
|
|
4257
|
-
expect(screen.getByText('error: error 2')).toBeInTheDocument();
|
|
4258
|
-
});
|
|
4259
|
-
expect(count).toBe(2);
|
|
4260
|
-
onlineMock.mockRestore();
|
|
4261
|
-
});
|
|
4262
|
-
});
|
|
4263
|
-
describe('networkMode offlineFirst', () => {
|
|
4264
|
-
it('offlineFirst queries should start fetching if you are offline, but pause retries', async () => {
|
|
4265
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
4266
|
-
const key = queryKey();
|
|
4267
|
-
let count = 0;
|
|
4268
|
-
function Page() {
|
|
4269
|
-
const state = createQuery(() => ({
|
|
4270
|
-
queryKey: key,
|
|
4271
|
-
queryFn: async () => {
|
|
4272
|
-
count++;
|
|
4273
|
-
await sleep(10);
|
|
4274
|
-
throw new Error('failed' + count);
|
|
4275
|
-
},
|
|
4276
|
-
retry: 2,
|
|
4277
|
-
retryDelay: 1,
|
|
4278
|
-
networkMode: 'offlineFirst',
|
|
4279
|
-
}));
|
|
4280
|
-
return (<div>
|
|
4281
|
-
<div>
|
|
4282
|
-
status: {state.status}, fetchStatus: {state.fetchStatus},
|
|
4283
|
-
failureCount: {state.failureCount}
|
|
4284
|
-
</div>
|
|
4285
|
-
<div>failureReason: {state.failureReason?.message ?? 'null'}</div>
|
|
4286
|
-
</div>);
|
|
4287
|
-
}
|
|
4288
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4289
|
-
<Page />
|
|
4290
|
-
</QueryClientProvider>));
|
|
4291
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: paused, failureCount: 1'));
|
|
4292
|
-
await waitFor(() => screen.getByText('failureReason: failed1'));
|
|
4293
|
-
expect(count).toBe(1);
|
|
4294
|
-
onlineMock.mockReturnValue(true);
|
|
4295
|
-
window.dispatchEvent(new Event('online'));
|
|
4296
|
-
await waitFor(() => screen.getByText('status: error, fetchStatus: idle, failureCount: 3'));
|
|
4297
|
-
await waitFor(() => screen.getByText('failureReason: failed3'));
|
|
4298
|
-
expect(count).toBe(3);
|
|
4299
|
-
onlineMock.mockRestore();
|
|
4300
|
-
});
|
|
4301
|
-
});
|
|
4302
|
-
it('it should have status=error on mount when a query has failed', async () => {
|
|
4303
|
-
const key = queryKey();
|
|
4304
|
-
const states = [];
|
|
4305
|
-
const error = new Error('oops');
|
|
4306
|
-
const queryFn = async () => {
|
|
4307
|
-
throw error;
|
|
4308
|
-
};
|
|
4309
|
-
function Page() {
|
|
4310
|
-
const state = createQuery(() => ({
|
|
4311
|
-
queryKey: key,
|
|
4312
|
-
queryFn,
|
|
4313
|
-
retry: false,
|
|
4314
|
-
retryOnMount: false,
|
|
4315
|
-
}));
|
|
4316
|
-
createRenderEffect(() => {
|
|
4317
|
-
states.push({ ...state });
|
|
4318
|
-
});
|
|
4319
|
-
return <></>;
|
|
4320
|
-
}
|
|
4321
|
-
await queryClient.prefetchQuery({ queryKey: key, queryFn });
|
|
4322
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4323
|
-
<Page />
|
|
4324
|
-
</QueryClientProvider>));
|
|
4325
|
-
await waitFor(() => expect(states).toHaveLength(1));
|
|
4326
|
-
expect(states[0]).toMatchObject({
|
|
4327
|
-
status: 'error',
|
|
4328
|
-
error,
|
|
4329
|
-
});
|
|
4330
|
-
});
|
|
4331
|
-
it('setQueryData - should respect updatedAt', async () => {
|
|
4332
|
-
const key = queryKey();
|
|
4333
|
-
function Page() {
|
|
4334
|
-
const state = createQuery(() => ({
|
|
4335
|
-
queryKey: key,
|
|
4336
|
-
queryFn: () => 'data',
|
|
4337
|
-
}));
|
|
4338
|
-
return (<div>
|
|
4339
|
-
<div>data: {state.data}</div>
|
|
4340
|
-
<div>dataUpdatedAt: {state.dataUpdatedAt}</div>
|
|
4341
|
-
<button onClick={() => queryClient.setQueryData(key, 'newData', {
|
|
4342
|
-
updatedAt: 100,
|
|
4343
|
-
})}>
|
|
4344
|
-
setQueryData
|
|
4345
|
-
</button>
|
|
4346
|
-
</div>);
|
|
4347
|
-
}
|
|
4348
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4349
|
-
<Page />
|
|
4350
|
-
</QueryClientProvider>));
|
|
4351
|
-
await waitFor(() => screen.getByText('data: data'));
|
|
4352
|
-
fireEvent.click(screen.getByRole('button', { name: /setQueryData/i }));
|
|
4353
|
-
await waitFor(() => screen.getByText('data: newData'));
|
|
4354
|
-
await waitFor(() => {
|
|
4355
|
-
expect(screen.getByText('dataUpdatedAt: 100')).toBeInTheDocument();
|
|
4356
|
-
});
|
|
4357
|
-
});
|
|
4358
|
-
it('errorUpdateCount should increased on each fetch failure', async () => {
|
|
4359
|
-
const key = queryKey();
|
|
4360
|
-
const error = new Error('oops');
|
|
4361
|
-
function Page() {
|
|
4362
|
-
const state = createQuery(() => ({
|
|
4363
|
-
queryKey: key,
|
|
4364
|
-
queryFn: async () => {
|
|
4365
|
-
throw error;
|
|
4366
|
-
},
|
|
4367
|
-
retry: false,
|
|
4368
|
-
}));
|
|
4369
|
-
return (<div>
|
|
4370
|
-
<button onClick={() => state.refetch()}>refetch</button>
|
|
4371
|
-
<span>data: {state.errorUpdateCount}</span>
|
|
4372
|
-
</div>);
|
|
4373
|
-
}
|
|
4374
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4375
|
-
<Page />
|
|
4376
|
-
</QueryClientProvider>));
|
|
4377
|
-
const fetchBtn = screen.getByRole('button', { name: 'refetch' });
|
|
4378
|
-
await waitFor(() => screen.getByText('data: 1'));
|
|
4379
|
-
fireEvent.click(fetchBtn);
|
|
4380
|
-
await waitFor(() => screen.getByText('data: 2'));
|
|
4381
|
-
fireEvent.click(fetchBtn);
|
|
4382
|
-
await waitFor(() => screen.getByText('data: 3'));
|
|
4383
|
-
});
|
|
4384
|
-
it('should use provided custom queryClient', async () => {
|
|
4385
|
-
const key = queryKey();
|
|
4386
|
-
const queryFn = () => {
|
|
4387
|
-
return Promise.resolve('custom client');
|
|
4388
|
-
};
|
|
4389
|
-
function Page() {
|
|
4390
|
-
const state = createQuery(() => ({ queryKey: key, queryFn }), () => queryClient);
|
|
4391
|
-
return (<div>
|
|
4392
|
-
<h1>Status: {state.data}</h1>
|
|
4393
|
-
</div>);
|
|
4394
|
-
}
|
|
4395
|
-
render(() => <Page />);
|
|
4396
|
-
await waitFor(() => screen.getByText('Status: custom client'));
|
|
4397
|
-
});
|
|
4398
|
-
});
|