@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.
Files changed (121) hide show
  1. package/build/lib/QueryClient.cjs +12 -0
  2. package/build/lib/QueryClient.cjs.map +1 -0
  3. package/build/lib/QueryClient.js +10 -0
  4. package/build/lib/QueryClient.js.map +1 -0
  5. package/build/lib/QueryClientProvider.cjs +35 -0
  6. package/build/lib/QueryClientProvider.cjs.map +1 -0
  7. package/build/lib/QueryClientProvider.js +31 -0
  8. package/build/lib/QueryClientProvider.js.map +1 -0
  9. package/build/lib/createBaseQuery.cjs +192 -0
  10. package/build/lib/createBaseQuery.cjs.map +1 -0
  11. package/build/lib/createBaseQuery.js +190 -0
  12. package/build/lib/createBaseQuery.js.map +1 -0
  13. package/build/lib/createInfiniteQuery.cjs +14 -0
  14. package/build/lib/createInfiniteQuery.cjs.map +1 -0
  15. package/build/lib/createInfiniteQuery.js +12 -0
  16. package/build/lib/createInfiniteQuery.js.map +1 -0
  17. package/build/lib/createMutation.cjs +44 -0
  18. package/build/lib/createMutation.cjs.map +1 -0
  19. package/build/lib/createMutation.js +42 -0
  20. package/build/lib/createMutation.js.map +1 -0
  21. package/build/lib/createQueries.cjs +52 -0
  22. package/build/lib/createQueries.cjs.map +1 -0
  23. package/build/lib/createQueries.js +50 -0
  24. package/build/lib/createQueries.js.map +1 -0
  25. package/build/lib/createQuery.cjs +16 -0
  26. package/build/lib/createQuery.cjs.map +1 -0
  27. package/build/lib/createQuery.js +13 -0
  28. package/build/lib/createQuery.js.map +1 -0
  29. package/build/lib/index.cjs.map +1 -0
  30. package/build/lib/index.js +11 -0
  31. package/build/lib/index.js.map +1 -0
  32. package/build/lib/setBatchUpdatesFn.cjs +7 -0
  33. package/build/lib/setBatchUpdatesFn.cjs.map +1 -0
  34. package/build/{source → lib}/setBatchUpdatesFn.js +2 -0
  35. package/build/lib/setBatchUpdatesFn.js.map +1 -0
  36. package/build/lib/useIsFetching.cjs +18 -0
  37. package/build/lib/useIsFetching.cjs.map +1 -0
  38. package/build/lib/useIsFetching.js +16 -0
  39. package/build/lib/useIsFetching.js.map +1 -0
  40. package/build/lib/useIsMutating.cjs +18 -0
  41. package/build/lib/useIsMutating.cjs.map +1 -0
  42. package/build/lib/useIsMutating.js +16 -0
  43. package/build/lib/useIsMutating.js.map +1 -0
  44. package/build/lib/utils.cjs +12 -0
  45. package/build/lib/utils.cjs.map +1 -0
  46. package/build/lib/utils.js +10 -0
  47. package/build/lib/utils.js.map +1 -0
  48. package/build/stats-html.html +6177 -0
  49. package/build/stats.json +605 -0
  50. package/package.json +6 -14
  51. package/build/source/QueryClient.js +0 -6
  52. package/build/source/QueryClientProvider.jsx +0 -21
  53. package/build/source/__tests__/QueryClientProvider.test.jsx +0 -120
  54. package/build/source/__tests__/createInfiniteQuery.test.jsx +0 -1360
  55. package/build/source/__tests__/createMutation.test.jsx +0 -867
  56. package/build/source/__tests__/createQueries.test.jsx +0 -590
  57. package/build/source/__tests__/createQuery.test.jsx +0 -4398
  58. package/build/source/__tests__/createQuery.types.test.jsx +0 -153
  59. package/build/source/__tests__/suspense.test.jsx +0 -659
  60. package/build/source/__tests__/transition.test.jsx +0 -42
  61. package/build/source/__tests__/useIsFetching.test.jsx +0 -190
  62. package/build/source/__tests__/useIsMutating.test.jsx +0 -196
  63. package/build/source/__tests__/utils.jsx +0 -50
  64. package/build/source/createBaseQuery.js +0 -173
  65. package/build/source/createInfiniteQuery.js +0 -8
  66. package/build/source/createMutation.js +0 -38
  67. package/build/source/createQueries.js +0 -38
  68. package/build/source/createQuery.js +0 -9
  69. package/build/source/index.js +0 -15
  70. package/build/source/types.js +0 -2
  71. package/build/source/useIsFetching.js +0 -12
  72. package/build/source/useIsMutating.js +0 -12
  73. package/build/source/utils.js +0 -7
  74. /package/build/{types → lib}/QueryClient.d.ts +0 -0
  75. /package/build/{types → lib}/QueryClient.d.ts.map +0 -0
  76. /package/build/{types → lib}/QueryClientProvider.d.ts +0 -0
  77. /package/build/{types → lib}/QueryClientProvider.d.ts.map +0 -0
  78. /package/build/{types → lib}/__tests__/QueryClientProvider.test.d.ts +0 -0
  79. /package/build/{types → lib}/__tests__/QueryClientProvider.test.d.ts.map +0 -0
  80. /package/build/{types → lib}/__tests__/createInfiniteQuery.test.d.ts +0 -0
  81. /package/build/{types → lib}/__tests__/createInfiniteQuery.test.d.ts.map +0 -0
  82. /package/build/{types → lib}/__tests__/createMutation.test.d.ts +0 -0
  83. /package/build/{types → lib}/__tests__/createMutation.test.d.ts.map +0 -0
  84. /package/build/{types → lib}/__tests__/createQueries.test.d.ts +0 -0
  85. /package/build/{types → lib}/__tests__/createQueries.test.d.ts.map +0 -0
  86. /package/build/{types → lib}/__tests__/createQuery.test.d.ts +0 -0
  87. /package/build/{types → lib}/__tests__/createQuery.test.d.ts.map +0 -0
  88. /package/build/{types → lib}/__tests__/createQuery.types.test.d.ts +0 -0
  89. /package/build/{types → lib}/__tests__/createQuery.types.test.d.ts.map +0 -0
  90. /package/build/{types → lib}/__tests__/suspense.test.d.ts +0 -0
  91. /package/build/{types → lib}/__tests__/suspense.test.d.ts.map +0 -0
  92. /package/build/{types → lib}/__tests__/transition.test.d.ts +0 -0
  93. /package/build/{types → lib}/__tests__/transition.test.d.ts.map +0 -0
  94. /package/build/{types → lib}/__tests__/useIsFetching.test.d.ts +0 -0
  95. /package/build/{types → lib}/__tests__/useIsFetching.test.d.ts.map +0 -0
  96. /package/build/{types → lib}/__tests__/useIsMutating.test.d.ts +0 -0
  97. /package/build/{types → lib}/__tests__/useIsMutating.test.d.ts.map +0 -0
  98. /package/build/{types → lib}/__tests__/utils.d.ts +0 -0
  99. /package/build/{types → lib}/__tests__/utils.d.ts.map +0 -0
  100. /package/build/{types → lib}/createBaseQuery.d.ts +0 -0
  101. /package/build/{types → lib}/createBaseQuery.d.ts.map +0 -0
  102. /package/build/{types → lib}/createInfiniteQuery.d.ts +0 -0
  103. /package/build/{types → lib}/createInfiniteQuery.d.ts.map +0 -0
  104. /package/build/{types → lib}/createMutation.d.ts +0 -0
  105. /package/build/{types → lib}/createMutation.d.ts.map +0 -0
  106. /package/build/{types → lib}/createQueries.d.ts +0 -0
  107. /package/build/{types → lib}/createQueries.d.ts.map +0 -0
  108. /package/build/{types → lib}/createQuery.d.ts +0 -0
  109. /package/build/{types → lib}/createQuery.d.ts.map +0 -0
  110. /package/build/{types → lib}/index.d.ts +0 -0
  111. /package/build/{types → lib}/index.d.ts.map +0 -0
  112. /package/build/{types → lib}/setBatchUpdatesFn.d.ts +0 -0
  113. /package/build/{types → lib}/setBatchUpdatesFn.d.ts.map +0 -0
  114. /package/build/{types → lib}/types.d.ts +0 -0
  115. /package/build/{types → lib}/types.d.ts.map +0 -0
  116. /package/build/{types → lib}/useIsFetching.d.ts +0 -0
  117. /package/build/{types → lib}/useIsFetching.d.ts.map +0 -0
  118. /package/build/{types → lib}/useIsMutating.d.ts +0 -0
  119. /package/build/{types → lib}/useIsMutating.d.ts.map +0 -0
  120. /package/build/{types → lib}/utils.d.ts +0 -0
  121. /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
- });