@tanstack/solid-query 5.0.0-alpha.2 → 5.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/index.js +73 -34
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +75 -37
- package/build/esm/index.js.map +1 -1
- package/build/source/QueryClient.js +6 -0
- package/build/source/__tests__/QueryClientProvider.test.jsx +2 -1
- package/build/source/__tests__/createInfiniteQuery.test.jsx +67 -20
- package/build/source/__tests__/createMutation.test.jsx +19 -18
- package/build/source/__tests__/createQueries.test.jsx +4 -91
- package/build/source/__tests__/createQuery.test.jsx +62 -271
- package/build/source/__tests__/suspense.test.jsx +3 -64
- package/build/source/__tests__/useIsFetching.test.jsx +2 -4
- package/build/source/__tests__/useIsMutating.test.jsx +25 -28
- package/build/source/__tests__/utils.jsx +4 -3
- package/build/source/createBaseQuery.js +45 -19
- package/build/source/createQueries.js +5 -5
- package/build/source/index.js +1 -0
- package/build/source/useIsFetching.js +5 -5
- package/build/source/useIsMutating.js +5 -5
- package/build/types/QueryClient.d.ts +29 -0
- package/build/types/QueryClientProvider.d.ts +1 -1
- package/build/types/__tests__/utils.d.ts +3 -4
- package/build/types/createBaseQuery.d.ts +3 -2
- package/build/types/createInfiniteQuery.d.ts +4 -2
- package/build/types/createMutation.d.ts +4 -2
- package/build/types/createQueries.d.ts +5 -4
- package/build/types/createQuery.d.ts +2 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/types.d.ts +2 -1
- package/build/types/useIsFetching.d.ts +3 -7
- package/build/types/useIsMutating.d.ts +3 -7
- package/build/umd/index.js +1 -1
- package/build/umd/index.js.map +1 -1
- package/package.json +5 -5
- package/src/QueryClient.ts +84 -0
- package/src/QueryClientProvider.tsx +1 -1
- package/src/__tests__/QueryClientProvider.test.tsx +2 -1
- package/src/__tests__/createInfiniteQuery.test.tsx +95 -34
- package/src/__tests__/createMutation.test.tsx +19 -18
- package/src/__tests__/createQueries.test.tsx +4 -97
- package/src/__tests__/createQuery.test.tsx +78 -344
- package/src/__tests__/suspense.test.tsx +3 -85
- package/src/__tests__/useIsFetching.test.tsx +2 -4
- package/src/__tests__/useIsMutating.test.tsx +32 -40
- package/src/__tests__/utils.tsx +4 -3
- package/src/createBaseQuery.ts +70 -25
- package/src/createInfiniteQuery.ts +3 -2
- package/src/createMutation.ts +4 -2
- package/src/createQueries.ts +9 -8
- package/src/createQuery.ts +4 -2
- package/src/index.ts +7 -0
- package/src/types.ts +4 -2
- package/src/useIsFetching.ts +10 -13
- package/src/useIsMutating.ts +10 -11
|
@@ -3,6 +3,8 @@ import { createEffect, createMemo, createRenderEffect, createSignal, ErrorBounda
|
|
|
3
3
|
import { fireEvent, render, screen, waitFor } from 'solid-testing-library';
|
|
4
4
|
import { createQuery, QueryCache, QueryClientProvider, keepPreviousData, } from '..';
|
|
5
5
|
import { Blink, createQueryClient, expectType, mockNavigatorOnLine, mockVisibilityState, queryKey, setActTimeout, sleep, } from './utils';
|
|
6
|
+
import { vi } from 'vitest';
|
|
7
|
+
import { reconcile } from 'solid-js/store';
|
|
6
8
|
describe('createQuery', () => {
|
|
7
9
|
const queryCache = new QueryCache();
|
|
8
10
|
const queryClient = createQueryClient({ queryCache });
|
|
@@ -40,20 +42,16 @@ describe('createQuery', () => {
|
|
|
40
42
|
createQuery(() => ({
|
|
41
43
|
queryKey: [key],
|
|
42
44
|
queryFn: async () => true,
|
|
43
|
-
onSuccess: (data) => expectType(data),
|
|
44
|
-
onSettled: (data) => expectType(data),
|
|
45
45
|
}));
|
|
46
46
|
// it should be possible to specify a union type as result type
|
|
47
47
|
const unionTypeSync = createQuery(() => ({
|
|
48
48
|
queryKey: key,
|
|
49
49
|
queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'),
|
|
50
|
-
onSuccess: (data) => expectType(data),
|
|
51
50
|
}));
|
|
52
51
|
expectType(unionTypeSync.data);
|
|
53
52
|
const unionTypeAsync = createQuery(() => ({
|
|
54
53
|
queryKey: key,
|
|
55
54
|
queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'),
|
|
56
|
-
onSuccess: (data) => expectType(data),
|
|
57
55
|
}));
|
|
58
56
|
expectType(unionTypeAsync.data);
|
|
59
57
|
// should error when the query function result does not match with the specified type
|
|
@@ -358,189 +356,6 @@ describe('createQuery', () => {
|
|
|
358
356
|
isFetchedAfterMount: true,
|
|
359
357
|
});
|
|
360
358
|
});
|
|
361
|
-
it('should call onSuccess after a query has been fetched', async () => {
|
|
362
|
-
const key = queryKey();
|
|
363
|
-
const states = [];
|
|
364
|
-
const onSuccess = jest.fn();
|
|
365
|
-
function Page() {
|
|
366
|
-
const state = createQuery(() => ({
|
|
367
|
-
queryKey: key,
|
|
368
|
-
queryFn: async () => {
|
|
369
|
-
await sleep(10);
|
|
370
|
-
return 'data';
|
|
371
|
-
},
|
|
372
|
-
onSuccess,
|
|
373
|
-
}));
|
|
374
|
-
createRenderEffect(() => {
|
|
375
|
-
states.push({ ...state });
|
|
376
|
-
});
|
|
377
|
-
return <div>data: {state.data}</div>;
|
|
378
|
-
}
|
|
379
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
380
|
-
<Page />
|
|
381
|
-
</QueryClientProvider>));
|
|
382
|
-
await screen.findByText('data: data');
|
|
383
|
-
expect(states.length).toBe(2);
|
|
384
|
-
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
385
|
-
expect(onSuccess).toHaveBeenCalledWith('data');
|
|
386
|
-
});
|
|
387
|
-
it('should call onSuccess after a disabled query has been fetched', async () => {
|
|
388
|
-
const key = queryKey();
|
|
389
|
-
const states = [];
|
|
390
|
-
const onSuccess = jest.fn();
|
|
391
|
-
function Page() {
|
|
392
|
-
const state = createQuery(() => ({
|
|
393
|
-
queryKey: key,
|
|
394
|
-
queryFn: () => 'data',
|
|
395
|
-
enabled: false,
|
|
396
|
-
onSuccess,
|
|
397
|
-
}));
|
|
398
|
-
createRenderEffect(() => {
|
|
399
|
-
states.push({ ...state });
|
|
400
|
-
});
|
|
401
|
-
createEffect(() => {
|
|
402
|
-
const refetch = state.refetch;
|
|
403
|
-
setActTimeout(() => {
|
|
404
|
-
refetch();
|
|
405
|
-
}, 10);
|
|
406
|
-
});
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
410
|
-
<Page />
|
|
411
|
-
</QueryClientProvider>));
|
|
412
|
-
await sleep(50);
|
|
413
|
-
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
414
|
-
expect(onSuccess).toHaveBeenCalledWith('data');
|
|
415
|
-
});
|
|
416
|
-
it('should not call onSuccess if a component has unmounted', async () => {
|
|
417
|
-
const key = queryKey();
|
|
418
|
-
const states = [];
|
|
419
|
-
const onSuccess = jest.fn();
|
|
420
|
-
function Page() {
|
|
421
|
-
const [show, setShow] = createSignal(true);
|
|
422
|
-
createEffect(() => {
|
|
423
|
-
setShow(false);
|
|
424
|
-
});
|
|
425
|
-
return <>{show() && <Component />}</>;
|
|
426
|
-
}
|
|
427
|
-
function Component() {
|
|
428
|
-
const state = createQuery(() => ({
|
|
429
|
-
queryKey: key,
|
|
430
|
-
queryFn: async () => {
|
|
431
|
-
await sleep(10);
|
|
432
|
-
return 'data';
|
|
433
|
-
},
|
|
434
|
-
onSuccess,
|
|
435
|
-
}));
|
|
436
|
-
createRenderEffect(() => {
|
|
437
|
-
states.push({ ...state });
|
|
438
|
-
});
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
442
|
-
<Page />
|
|
443
|
-
</QueryClientProvider>));
|
|
444
|
-
await sleep(50);
|
|
445
|
-
expect(states.length).toBe(1);
|
|
446
|
-
expect(onSuccess).toHaveBeenCalledTimes(0);
|
|
447
|
-
});
|
|
448
|
-
it('should call onError after a query has been fetched with an error', async () => {
|
|
449
|
-
const key = queryKey();
|
|
450
|
-
const states = [];
|
|
451
|
-
const onError = jest.fn();
|
|
452
|
-
function Page() {
|
|
453
|
-
const state = createQuery(() => ({
|
|
454
|
-
queryKey: key,
|
|
455
|
-
queryFn: () => Promise.reject(new Error('error')),
|
|
456
|
-
retry: false,
|
|
457
|
-
onError,
|
|
458
|
-
}));
|
|
459
|
-
createRenderEffect(() => {
|
|
460
|
-
states.push({ ...state });
|
|
461
|
-
});
|
|
462
|
-
return null;
|
|
463
|
-
}
|
|
464
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
465
|
-
<Page />
|
|
466
|
-
</QueryClientProvider>));
|
|
467
|
-
await sleep(10);
|
|
468
|
-
expect(states.length).toBe(2);
|
|
469
|
-
expect(onError).toHaveBeenCalledTimes(1);
|
|
470
|
-
expect(onError).toHaveBeenCalledWith(new Error('error'));
|
|
471
|
-
});
|
|
472
|
-
it('should not call onError when receiving a CancelledError', async () => {
|
|
473
|
-
const key = queryKey();
|
|
474
|
-
const onError = jest.fn();
|
|
475
|
-
function Page() {
|
|
476
|
-
const state = createQuery(() => ({
|
|
477
|
-
queryKey: key,
|
|
478
|
-
queryFn: async () => {
|
|
479
|
-
await sleep(10);
|
|
480
|
-
return 23;
|
|
481
|
-
},
|
|
482
|
-
onError,
|
|
483
|
-
}));
|
|
484
|
-
return (<span>
|
|
485
|
-
status: {state.status}, fetchStatus: {state.fetchStatus}
|
|
486
|
-
</span>);
|
|
487
|
-
}
|
|
488
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
489
|
-
<Page />
|
|
490
|
-
</QueryClientProvider>));
|
|
491
|
-
await sleep(5);
|
|
492
|
-
await queryClient.cancelQueries({ queryKey: key });
|
|
493
|
-
// query cancellation will reset the query to it's initial state
|
|
494
|
-
await waitFor(() => screen.getByText('status: pending, fetchStatus: idle'));
|
|
495
|
-
expect(onError).not.toHaveBeenCalled();
|
|
496
|
-
});
|
|
497
|
-
it('should call onSettled after a query has been fetched', async () => {
|
|
498
|
-
const key = queryKey();
|
|
499
|
-
const states = [];
|
|
500
|
-
const onSettled = jest.fn();
|
|
501
|
-
function Page() {
|
|
502
|
-
const state = createQuery(() => ({
|
|
503
|
-
queryKey: key,
|
|
504
|
-
queryFn: () => 'data',
|
|
505
|
-
onSettled,
|
|
506
|
-
}));
|
|
507
|
-
createRenderEffect(() => {
|
|
508
|
-
states.push({ ...state });
|
|
509
|
-
});
|
|
510
|
-
return null;
|
|
511
|
-
}
|
|
512
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
513
|
-
<Page />
|
|
514
|
-
</QueryClientProvider>));
|
|
515
|
-
await sleep(10);
|
|
516
|
-
expect(states.length).toBe(2);
|
|
517
|
-
expect(onSettled).toHaveBeenCalledTimes(1);
|
|
518
|
-
expect(onSettled).toHaveBeenCalledWith('data', null);
|
|
519
|
-
});
|
|
520
|
-
it('should call onSettled after a query has been fetched with an error', async () => {
|
|
521
|
-
const key = queryKey();
|
|
522
|
-
const states = [];
|
|
523
|
-
const onSettled = jest.fn();
|
|
524
|
-
function Page() {
|
|
525
|
-
const state = createQuery(() => ({
|
|
526
|
-
queryKey: key,
|
|
527
|
-
queryFn: () => Promise.reject('error'),
|
|
528
|
-
retry: false,
|
|
529
|
-
onSettled,
|
|
530
|
-
}));
|
|
531
|
-
createRenderEffect(() => {
|
|
532
|
-
states.push({ ...state });
|
|
533
|
-
});
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
537
|
-
<Page />
|
|
538
|
-
</QueryClientProvider>));
|
|
539
|
-
await sleep(10);
|
|
540
|
-
expect(states.length).toBe(2);
|
|
541
|
-
expect(onSettled).toHaveBeenCalledTimes(1);
|
|
542
|
-
expect(onSettled).toHaveBeenCalledWith(undefined, 'error');
|
|
543
|
-
});
|
|
544
359
|
it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => {
|
|
545
360
|
const key = queryKey();
|
|
546
361
|
let fetchCount = 0;
|
|
@@ -964,7 +779,6 @@ describe('createQuery', () => {
|
|
|
964
779
|
count++;
|
|
965
780
|
return count === 1 ? result1 : result2;
|
|
966
781
|
},
|
|
967
|
-
notifyOnChangeProps: 'all',
|
|
968
782
|
}));
|
|
969
783
|
createRenderEffect(() => {
|
|
970
784
|
states.push({ ...state });
|
|
@@ -991,9 +805,8 @@ describe('createQuery', () => {
|
|
|
991
805
|
const newTodo2 = newTodos?.[1];
|
|
992
806
|
expect(todos).toEqual(result1);
|
|
993
807
|
expect(newTodos).toEqual(result2);
|
|
994
|
-
expect(newTodos).not.toBe(todos);
|
|
995
808
|
expect(newTodo1).toBe(todo1);
|
|
996
|
-
expect(newTodo2).
|
|
809
|
+
expect(newTodo2).toBe(todo2);
|
|
997
810
|
return null;
|
|
998
811
|
});
|
|
999
812
|
it('should use query function from hook when the existing query does not have a query function', async () => {
|
|
@@ -1751,45 +1564,6 @@ describe('createQuery', () => {
|
|
|
1751
1564
|
// There wiil only be one pass
|
|
1752
1565
|
expect(renders).toBe(1);
|
|
1753
1566
|
});
|
|
1754
|
-
it('should batch re-renders including hook callbacks', async () => {
|
|
1755
|
-
const key = queryKey();
|
|
1756
|
-
let renders = 0;
|
|
1757
|
-
let callbackCount = 0;
|
|
1758
|
-
const queryFn = async () => {
|
|
1759
|
-
await sleep(10);
|
|
1760
|
-
return 'data';
|
|
1761
|
-
};
|
|
1762
|
-
function Page() {
|
|
1763
|
-
const [count, setCount] = createSignal(0);
|
|
1764
|
-
createQuery(() => ({
|
|
1765
|
-
queryKey: key,
|
|
1766
|
-
queryFn,
|
|
1767
|
-
onSuccess: () => {
|
|
1768
|
-
setCount((x) => x + 1);
|
|
1769
|
-
},
|
|
1770
|
-
}));
|
|
1771
|
-
createQuery(() => ({
|
|
1772
|
-
queryKey: key,
|
|
1773
|
-
queryFn,
|
|
1774
|
-
onSuccess: () => {
|
|
1775
|
-
setCount((x) => x + 1);
|
|
1776
|
-
},
|
|
1777
|
-
}));
|
|
1778
|
-
createEffect(() => {
|
|
1779
|
-
renders++;
|
|
1780
|
-
callbackCount = count();
|
|
1781
|
-
});
|
|
1782
|
-
return <div>count: {count()}</div>;
|
|
1783
|
-
}
|
|
1784
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
1785
|
-
<Page />
|
|
1786
|
-
</QueryClientProvider>));
|
|
1787
|
-
await waitFor(() => screen.getByText('count: 2'));
|
|
1788
|
-
// Should be 3 instead of 5
|
|
1789
|
-
expect(renders).toBe(3);
|
|
1790
|
-
// Both callbacks should have been executed
|
|
1791
|
-
expect(callbackCount).toBe(2);
|
|
1792
|
-
});
|
|
1793
1567
|
it('should render latest data even if react has discarded certain renders', async () => {
|
|
1794
1568
|
const key = queryKey();
|
|
1795
1569
|
function Page() {
|
|
@@ -1887,7 +1661,7 @@ describe('createQuery', () => {
|
|
|
1887
1661
|
});
|
|
1888
1662
|
it('should not refetch query on focus when `enabled` is set to `false`', async () => {
|
|
1889
1663
|
const key = queryKey();
|
|
1890
|
-
const queryFn =
|
|
1664
|
+
const queryFn = vi.fn().mockReturnValue('data');
|
|
1891
1665
|
function Page() {
|
|
1892
1666
|
const { data = 'default' } = createQuery(() => ({
|
|
1893
1667
|
queryKey: key,
|
|
@@ -2500,6 +2274,7 @@ describe('createQuery', () => {
|
|
|
2500
2274
|
queryFn: () => ({ count: 10 }),
|
|
2501
2275
|
staleTime: Infinity,
|
|
2502
2276
|
initialData: () => ({ count: count() }),
|
|
2277
|
+
reconcile: false,
|
|
2503
2278
|
}));
|
|
2504
2279
|
createRenderEffect(() => {
|
|
2505
2280
|
states.push({ ...state });
|
|
@@ -2523,7 +2298,7 @@ describe('createQuery', () => {
|
|
|
2523
2298
|
});
|
|
2524
2299
|
it('should retry specified number of times', async () => {
|
|
2525
2300
|
const key = queryKey();
|
|
2526
|
-
const queryFn =
|
|
2301
|
+
const queryFn = vi.fn();
|
|
2527
2302
|
queryFn.mockImplementation(() => {
|
|
2528
2303
|
return Promise.reject(new Error('Error test Barrett'));
|
|
2529
2304
|
});
|
|
@@ -2552,7 +2327,7 @@ describe('createQuery', () => {
|
|
|
2552
2327
|
});
|
|
2553
2328
|
it('should not retry if retry function `false`', async () => {
|
|
2554
2329
|
const key = queryKey();
|
|
2555
|
-
const queryFn =
|
|
2330
|
+
const queryFn = vi.fn();
|
|
2556
2331
|
queryFn.mockImplementationOnce(() => {
|
|
2557
2332
|
return Promise.reject(new Error('Error test Tanner'));
|
|
2558
2333
|
});
|
|
@@ -2585,7 +2360,7 @@ describe('createQuery', () => {
|
|
|
2585
2360
|
});
|
|
2586
2361
|
it('should extract retryDelay from error', async () => {
|
|
2587
2362
|
const key = queryKey();
|
|
2588
|
-
const queryFn =
|
|
2363
|
+
const queryFn = vi.fn();
|
|
2589
2364
|
queryFn.mockImplementation(() => {
|
|
2590
2365
|
return Promise.reject({ delay: 50 });
|
|
2591
2366
|
});
|
|
@@ -2747,9 +2522,9 @@ describe('createQuery', () => {
|
|
|
2747
2522
|
it('should refetch if stale after a prefetch', async () => {
|
|
2748
2523
|
const key = queryKey();
|
|
2749
2524
|
const states = [];
|
|
2750
|
-
const queryFn =
|
|
2525
|
+
const queryFn = vi.fn();
|
|
2751
2526
|
queryFn.mockImplementation(() => 'data');
|
|
2752
|
-
const prefetchQueryFn =
|
|
2527
|
+
const prefetchQueryFn = vi.fn();
|
|
2753
2528
|
prefetchQueryFn.mockImplementation(() => 'not yet...');
|
|
2754
2529
|
await queryClient.prefetchQuery({
|
|
2755
2530
|
queryKey: key,
|
|
@@ -2773,9 +2548,9 @@ describe('createQuery', () => {
|
|
|
2773
2548
|
});
|
|
2774
2549
|
it('should not refetch if not stale after a prefetch', async () => {
|
|
2775
2550
|
const key = queryKey();
|
|
2776
|
-
const queryFn =
|
|
2551
|
+
const queryFn = vi.fn();
|
|
2777
2552
|
queryFn.mockImplementation(() => 'data');
|
|
2778
|
-
const prefetchQueryFn =
|
|
2553
|
+
const prefetchQueryFn = vi.fn();
|
|
2779
2554
|
prefetchQueryFn.mockImplementation(async () => {
|
|
2780
2555
|
await sleep(10);
|
|
2781
2556
|
return 'not yet...';
|
|
@@ -2977,7 +2752,7 @@ describe('createQuery', () => {
|
|
|
2977
2752
|
});
|
|
2978
2753
|
it('it should support enabled:false in query object syntax', async () => {
|
|
2979
2754
|
const key = queryKey();
|
|
2980
|
-
const queryFn =
|
|
2755
|
+
const queryFn = vi.fn();
|
|
2981
2756
|
queryFn.mockImplementation(() => 'data');
|
|
2982
2757
|
function Page() {
|
|
2983
2758
|
const { fetchStatus } = createQuery(() => ({
|
|
@@ -3028,7 +2803,7 @@ describe('createQuery', () => {
|
|
|
3028
2803
|
<Page />
|
|
3029
2804
|
</QueryClientProvider>));
|
|
3030
2805
|
await waitFor(() => screen.getByText('fetched data'));
|
|
3031
|
-
const setTimeoutSpy =
|
|
2806
|
+
const setTimeoutSpy = vi.spyOn(window, 'setTimeout');
|
|
3032
2807
|
result.unmount();
|
|
3033
2808
|
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
|
3034
2809
|
});
|
|
@@ -3046,14 +2821,14 @@ describe('createQuery', () => {
|
|
|
3046
2821
|
<Page />
|
|
3047
2822
|
</QueryClientProvider>));
|
|
3048
2823
|
await waitFor(() => screen.getByText('fetched data'));
|
|
3049
|
-
const setTimeoutSpy =
|
|
2824
|
+
const setTimeoutSpy = vi.spyOn(window, 'setTimeout');
|
|
3050
2825
|
result.unmount();
|
|
3051
2826
|
expect(setTimeoutSpy).toHaveBeenLastCalledWith(expect.any(Function), 1000 * 60 * 10);
|
|
3052
2827
|
});
|
|
3053
2828
|
it('should not cause memo churn when data does not change', async () => {
|
|
3054
2829
|
const key = queryKey();
|
|
3055
|
-
const queryFn =
|
|
3056
|
-
const memoFn =
|
|
2830
|
+
const queryFn = vi.fn().mockReturnValue('data');
|
|
2831
|
+
const memoFn = vi.fn();
|
|
3057
2832
|
function Page() {
|
|
3058
2833
|
const result = createQuery(() => ({
|
|
3059
2834
|
queryKey: key,
|
|
@@ -3234,7 +3009,7 @@ describe('createQuery', () => {
|
|
|
3234
3009
|
});
|
|
3235
3010
|
it('should refetch if any query instance becomes enabled', async () => {
|
|
3236
3011
|
const key = queryKey();
|
|
3237
|
-
const queryFn =
|
|
3012
|
+
const queryFn = vi.fn().mockReturnValue('data');
|
|
3238
3013
|
function Disabled() {
|
|
3239
3014
|
createQuery(() => ({ queryKey: key, queryFn, enabled: false }));
|
|
3240
3015
|
return null;
|
|
@@ -3502,12 +3277,53 @@ describe('createQuery', () => {
|
|
|
3502
3277
|
// effect should not be triggered again due to structural sharing
|
|
3503
3278
|
expect(states).toHaveLength(1);
|
|
3504
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
|
+
});
|
|
3505
3321
|
it('should cancel the query function when there are no more subscriptions', async () => {
|
|
3506
3322
|
const key = queryKey();
|
|
3507
|
-
let cancelFn =
|
|
3323
|
+
let cancelFn = vi.fn();
|
|
3508
3324
|
const queryFn = ({ signal }) => {
|
|
3509
3325
|
const promise = new Promise((resolve, reject) => {
|
|
3510
|
-
cancelFn =
|
|
3326
|
+
cancelFn = vi.fn(() => reject('Cancelled'));
|
|
3511
3327
|
signal?.addEventListener('abort', cancelFn);
|
|
3512
3328
|
sleep(20).then(() => resolve('OK'));
|
|
3513
3329
|
});
|
|
@@ -3767,7 +3583,7 @@ describe('createQuery', () => {
|
|
|
3767
3583
|
expect(renders).toBe(hashes);
|
|
3768
3584
|
});
|
|
3769
3585
|
it('should refetch when changed enabled to true in error state', async () => {
|
|
3770
|
-
const queryFn =
|
|
3586
|
+
const queryFn = vi.fn();
|
|
3771
3587
|
queryFn.mockImplementation(async () => {
|
|
3772
3588
|
await sleep(10);
|
|
3773
3589
|
return Promise.reject(new Error('Suspense Error Bingo'));
|
|
@@ -4512,31 +4328,6 @@ describe('createQuery', () => {
|
|
|
4512
4328
|
error,
|
|
4513
4329
|
});
|
|
4514
4330
|
});
|
|
4515
|
-
it('setQueryData - should not call onSuccess callback of active observers', async () => {
|
|
4516
|
-
const key = queryKey();
|
|
4517
|
-
const onSuccess = jest.fn();
|
|
4518
|
-
function Page() {
|
|
4519
|
-
const state = createQuery(() => ({
|
|
4520
|
-
queryKey: key,
|
|
4521
|
-
queryFn: () => 'data',
|
|
4522
|
-
onSuccess,
|
|
4523
|
-
}));
|
|
4524
|
-
return (<div>
|
|
4525
|
-
<div>data: {state.data}</div>
|
|
4526
|
-
<button onClick={() => queryClient.setQueryData(key, 'newData')}>
|
|
4527
|
-
setQueryData
|
|
4528
|
-
</button>
|
|
4529
|
-
</div>);
|
|
4530
|
-
}
|
|
4531
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
4532
|
-
<Page />
|
|
4533
|
-
</QueryClientProvider>));
|
|
4534
|
-
await waitFor(() => screen.getByText('data: data'));
|
|
4535
|
-
fireEvent.click(screen.getByRole('button', { name: /setQueryData/i }));
|
|
4536
|
-
await waitFor(() => screen.getByText('data: newData'));
|
|
4537
|
-
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
4538
|
-
expect(onSuccess).toHaveBeenCalledWith('data');
|
|
4539
|
-
});
|
|
4540
4331
|
it('setQueryData - should respect updatedAt', async () => {
|
|
4541
4332
|
const key = queryKey();
|
|
4542
4333
|
function Page() {
|
|
@@ -2,6 +2,7 @@ import { fireEvent, render, screen, waitFor } from 'solid-testing-library';
|
|
|
2
2
|
import { createRenderEffect, createSignal, ErrorBoundary, on, Show, Suspense, } from 'solid-js';
|
|
3
3
|
import { createInfiniteQuery, createQuery, QueryCache, QueryClientProvider, } from '..';
|
|
4
4
|
import { createQueryClient, queryKey, sleep } from './utils';
|
|
5
|
+
import { vi } from 'vitest';
|
|
5
6
|
describe("useQuery's in Suspense mode", () => {
|
|
6
7
|
const queryCache = new QueryCache();
|
|
7
8
|
const queryClient = createQueryClient({ queryCache });
|
|
@@ -91,7 +92,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
91
92
|
});
|
|
92
93
|
it('should not call the queryFn twice when used in Suspense mode', async () => {
|
|
93
94
|
const key = queryKey();
|
|
94
|
-
const queryFn =
|
|
95
|
+
const queryFn = vi.fn();
|
|
95
96
|
queryFn.mockImplementation(() => {
|
|
96
97
|
sleep(10);
|
|
97
98
|
return 'data';
|
|
@@ -139,68 +140,6 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
139
140
|
expect(screen.queryByText('rendered')).toBeNull();
|
|
140
141
|
expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(0);
|
|
141
142
|
});
|
|
142
|
-
it('should call onSuccess on the first successful call', async () => {
|
|
143
|
-
const key = queryKey();
|
|
144
|
-
const successFn = jest.fn();
|
|
145
|
-
function Page() {
|
|
146
|
-
createQuery(() => ({
|
|
147
|
-
queryKey: [key],
|
|
148
|
-
queryFn: async () => {
|
|
149
|
-
await sleep(10);
|
|
150
|
-
return key;
|
|
151
|
-
},
|
|
152
|
-
suspense: true,
|
|
153
|
-
select: () => 'selected',
|
|
154
|
-
onSuccess: successFn,
|
|
155
|
-
}));
|
|
156
|
-
return <>rendered</>;
|
|
157
|
-
}
|
|
158
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
159
|
-
<Suspense fallback="loading">
|
|
160
|
-
<Page />
|
|
161
|
-
</Suspense>
|
|
162
|
-
</QueryClientProvider>));
|
|
163
|
-
await waitFor(() => screen.getByText('rendered'));
|
|
164
|
-
await waitFor(() => expect(successFn).toHaveBeenCalledTimes(1));
|
|
165
|
-
await waitFor(() => expect(successFn).toHaveBeenCalledWith('selected'));
|
|
166
|
-
});
|
|
167
|
-
it('should call every onSuccess handler within a suspense boundary', async () => {
|
|
168
|
-
const key = queryKey();
|
|
169
|
-
const successFn1 = jest.fn();
|
|
170
|
-
const successFn2 = jest.fn();
|
|
171
|
-
function FirstComponent() {
|
|
172
|
-
createQuery(() => ({
|
|
173
|
-
queryKey: key,
|
|
174
|
-
queryFn: () => {
|
|
175
|
-
sleep(10);
|
|
176
|
-
return 'data';
|
|
177
|
-
},
|
|
178
|
-
onSuccess: successFn1,
|
|
179
|
-
}));
|
|
180
|
-
return <span>first</span>;
|
|
181
|
-
}
|
|
182
|
-
function SecondComponent() {
|
|
183
|
-
createQuery(() => ({
|
|
184
|
-
queryKey: key,
|
|
185
|
-
queryFn: () => {
|
|
186
|
-
sleep(10);
|
|
187
|
-
return 'data';
|
|
188
|
-
},
|
|
189
|
-
onSuccess: successFn2,
|
|
190
|
-
}));
|
|
191
|
-
return <span>second</span>;
|
|
192
|
-
}
|
|
193
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
194
|
-
<Suspense fallback="loading">
|
|
195
|
-
<FirstComponent />
|
|
196
|
-
<SecondComponent />
|
|
197
|
-
</Suspense>
|
|
198
|
-
,
|
|
199
|
-
</QueryClientProvider>));
|
|
200
|
-
await waitFor(() => screen.getByText('second'));
|
|
201
|
-
await waitFor(() => expect(successFn1).toHaveBeenCalledTimes(1));
|
|
202
|
-
await waitFor(() => expect(successFn2).toHaveBeenCalledTimes(1));
|
|
203
|
-
});
|
|
204
143
|
// https://github.com/tannerlinsley/react-query/issues/468
|
|
205
144
|
it('should reset error state if new component instances are mounted', async () => {
|
|
206
145
|
const key = queryKey();
|
|
@@ -514,7 +453,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
514
453
|
});
|
|
515
454
|
it('should not call the queryFn when not enabled', async () => {
|
|
516
455
|
const key = queryKey();
|
|
517
|
-
const queryFn =
|
|
456
|
+
const queryFn = vi.fn();
|
|
518
457
|
queryFn.mockImplementation(async () => {
|
|
519
458
|
await sleep(10);
|
|
520
459
|
return '23';
|
|
@@ -120,9 +120,7 @@ describe('useIsFetching', () => {
|
|
|
120
120
|
function Page() {
|
|
121
121
|
const [started, setStarted] = createSignal(false);
|
|
122
122
|
const isFetching = useIsFetching(() => ({
|
|
123
|
-
|
|
124
|
-
queryKey: key1,
|
|
125
|
-
},
|
|
123
|
+
queryKey: key1,
|
|
126
124
|
}));
|
|
127
125
|
createRenderEffect(() => {
|
|
128
126
|
isFetchings.push(isFetching());
|
|
@@ -181,7 +179,7 @@ describe('useIsFetching', () => {
|
|
|
181
179
|
return 'test';
|
|
182
180
|
},
|
|
183
181
|
}), () => queryClient);
|
|
184
|
-
const isFetching = useIsFetching(() =>
|
|
182
|
+
const isFetching = useIsFetching(undefined, () => queryClient);
|
|
185
183
|
return (<div>
|
|
186
184
|
<div>isFetching: {isFetching}</div>
|
|
187
185
|
</div>);
|