@tanstack/solid-query 5.0.0-alpha.2 → 5.0.0-alpha.20
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 +2 -18
- package/build/source/__tests__/createQuery.test.jsx +70 -28
- package/build/source/__tests__/suspense.test.jsx +6 -5
- 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 +2 -24
- package/src/__tests__/createQuery.test.tsx +86 -29
- package/src/__tests__/suspense.test.tsx +6 -5
- 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 });
|
|
@@ -361,7 +363,7 @@ describe('createQuery', () => {
|
|
|
361
363
|
it('should call onSuccess after a query has been fetched', async () => {
|
|
362
364
|
const key = queryKey();
|
|
363
365
|
const states = [];
|
|
364
|
-
const onSuccess =
|
|
366
|
+
const onSuccess = vi.fn();
|
|
365
367
|
function Page() {
|
|
366
368
|
const state = createQuery(() => ({
|
|
367
369
|
queryKey: key,
|
|
@@ -387,7 +389,7 @@ describe('createQuery', () => {
|
|
|
387
389
|
it('should call onSuccess after a disabled query has been fetched', async () => {
|
|
388
390
|
const key = queryKey();
|
|
389
391
|
const states = [];
|
|
390
|
-
const onSuccess =
|
|
392
|
+
const onSuccess = vi.fn();
|
|
391
393
|
function Page() {
|
|
392
394
|
const state = createQuery(() => ({
|
|
393
395
|
queryKey: key,
|
|
@@ -416,7 +418,7 @@ describe('createQuery', () => {
|
|
|
416
418
|
it('should not call onSuccess if a component has unmounted', async () => {
|
|
417
419
|
const key = queryKey();
|
|
418
420
|
const states = [];
|
|
419
|
-
const onSuccess =
|
|
421
|
+
const onSuccess = vi.fn();
|
|
420
422
|
function Page() {
|
|
421
423
|
const [show, setShow] = createSignal(true);
|
|
422
424
|
createEffect(() => {
|
|
@@ -448,7 +450,7 @@ describe('createQuery', () => {
|
|
|
448
450
|
it('should call onError after a query has been fetched with an error', async () => {
|
|
449
451
|
const key = queryKey();
|
|
450
452
|
const states = [];
|
|
451
|
-
const onError =
|
|
453
|
+
const onError = vi.fn();
|
|
452
454
|
function Page() {
|
|
453
455
|
const state = createQuery(() => ({
|
|
454
456
|
queryKey: key,
|
|
@@ -471,7 +473,7 @@ describe('createQuery', () => {
|
|
|
471
473
|
});
|
|
472
474
|
it('should not call onError when receiving a CancelledError', async () => {
|
|
473
475
|
const key = queryKey();
|
|
474
|
-
const onError =
|
|
476
|
+
const onError = vi.fn();
|
|
475
477
|
function Page() {
|
|
476
478
|
const state = createQuery(() => ({
|
|
477
479
|
queryKey: key,
|
|
@@ -497,7 +499,7 @@ describe('createQuery', () => {
|
|
|
497
499
|
it('should call onSettled after a query has been fetched', async () => {
|
|
498
500
|
const key = queryKey();
|
|
499
501
|
const states = [];
|
|
500
|
-
const onSettled =
|
|
502
|
+
const onSettled = vi.fn();
|
|
501
503
|
function Page() {
|
|
502
504
|
const state = createQuery(() => ({
|
|
503
505
|
queryKey: key,
|
|
@@ -520,7 +522,7 @@ describe('createQuery', () => {
|
|
|
520
522
|
it('should call onSettled after a query has been fetched with an error', async () => {
|
|
521
523
|
const key = queryKey();
|
|
522
524
|
const states = [];
|
|
523
|
-
const onSettled =
|
|
525
|
+
const onSettled = vi.fn();
|
|
524
526
|
function Page() {
|
|
525
527
|
const state = createQuery(() => ({
|
|
526
528
|
queryKey: key,
|
|
@@ -964,7 +966,6 @@ describe('createQuery', () => {
|
|
|
964
966
|
count++;
|
|
965
967
|
return count === 1 ? result1 : result2;
|
|
966
968
|
},
|
|
967
|
-
notifyOnChangeProps: 'all',
|
|
968
969
|
}));
|
|
969
970
|
createRenderEffect(() => {
|
|
970
971
|
states.push({ ...state });
|
|
@@ -991,9 +992,8 @@ describe('createQuery', () => {
|
|
|
991
992
|
const newTodo2 = newTodos?.[1];
|
|
992
993
|
expect(todos).toEqual(result1);
|
|
993
994
|
expect(newTodos).toEqual(result2);
|
|
994
|
-
expect(newTodos).not.toBe(todos);
|
|
995
995
|
expect(newTodo1).toBe(todo1);
|
|
996
|
-
expect(newTodo2).
|
|
996
|
+
expect(newTodo2).toBe(todo2);
|
|
997
997
|
return null;
|
|
998
998
|
});
|
|
999
999
|
it('should use query function from hook when the existing query does not have a query function', async () => {
|
|
@@ -1887,7 +1887,7 @@ describe('createQuery', () => {
|
|
|
1887
1887
|
});
|
|
1888
1888
|
it('should not refetch query on focus when `enabled` is set to `false`', async () => {
|
|
1889
1889
|
const key = queryKey();
|
|
1890
|
-
const queryFn =
|
|
1890
|
+
const queryFn = vi.fn().mockReturnValue('data');
|
|
1891
1891
|
function Page() {
|
|
1892
1892
|
const { data = 'default' } = createQuery(() => ({
|
|
1893
1893
|
queryKey: key,
|
|
@@ -2500,6 +2500,7 @@ describe('createQuery', () => {
|
|
|
2500
2500
|
queryFn: () => ({ count: 10 }),
|
|
2501
2501
|
staleTime: Infinity,
|
|
2502
2502
|
initialData: () => ({ count: count() }),
|
|
2503
|
+
reconcile: false,
|
|
2503
2504
|
}));
|
|
2504
2505
|
createRenderEffect(() => {
|
|
2505
2506
|
states.push({ ...state });
|
|
@@ -2523,7 +2524,7 @@ describe('createQuery', () => {
|
|
|
2523
2524
|
});
|
|
2524
2525
|
it('should retry specified number of times', async () => {
|
|
2525
2526
|
const key = queryKey();
|
|
2526
|
-
const queryFn =
|
|
2527
|
+
const queryFn = vi.fn();
|
|
2527
2528
|
queryFn.mockImplementation(() => {
|
|
2528
2529
|
return Promise.reject(new Error('Error test Barrett'));
|
|
2529
2530
|
});
|
|
@@ -2552,7 +2553,7 @@ describe('createQuery', () => {
|
|
|
2552
2553
|
});
|
|
2553
2554
|
it('should not retry if retry function `false`', async () => {
|
|
2554
2555
|
const key = queryKey();
|
|
2555
|
-
const queryFn =
|
|
2556
|
+
const queryFn = vi.fn();
|
|
2556
2557
|
queryFn.mockImplementationOnce(() => {
|
|
2557
2558
|
return Promise.reject(new Error('Error test Tanner'));
|
|
2558
2559
|
});
|
|
@@ -2585,7 +2586,7 @@ describe('createQuery', () => {
|
|
|
2585
2586
|
});
|
|
2586
2587
|
it('should extract retryDelay from error', async () => {
|
|
2587
2588
|
const key = queryKey();
|
|
2588
|
-
const queryFn =
|
|
2589
|
+
const queryFn = vi.fn();
|
|
2589
2590
|
queryFn.mockImplementation(() => {
|
|
2590
2591
|
return Promise.reject({ delay: 50 });
|
|
2591
2592
|
});
|
|
@@ -2747,9 +2748,9 @@ describe('createQuery', () => {
|
|
|
2747
2748
|
it('should refetch if stale after a prefetch', async () => {
|
|
2748
2749
|
const key = queryKey();
|
|
2749
2750
|
const states = [];
|
|
2750
|
-
const queryFn =
|
|
2751
|
+
const queryFn = vi.fn();
|
|
2751
2752
|
queryFn.mockImplementation(() => 'data');
|
|
2752
|
-
const prefetchQueryFn =
|
|
2753
|
+
const prefetchQueryFn = vi.fn();
|
|
2753
2754
|
prefetchQueryFn.mockImplementation(() => 'not yet...');
|
|
2754
2755
|
await queryClient.prefetchQuery({
|
|
2755
2756
|
queryKey: key,
|
|
@@ -2773,9 +2774,9 @@ describe('createQuery', () => {
|
|
|
2773
2774
|
});
|
|
2774
2775
|
it('should not refetch if not stale after a prefetch', async () => {
|
|
2775
2776
|
const key = queryKey();
|
|
2776
|
-
const queryFn =
|
|
2777
|
+
const queryFn = vi.fn();
|
|
2777
2778
|
queryFn.mockImplementation(() => 'data');
|
|
2778
|
-
const prefetchQueryFn =
|
|
2779
|
+
const prefetchQueryFn = vi.fn();
|
|
2779
2780
|
prefetchQueryFn.mockImplementation(async () => {
|
|
2780
2781
|
await sleep(10);
|
|
2781
2782
|
return 'not yet...';
|
|
@@ -2977,7 +2978,7 @@ describe('createQuery', () => {
|
|
|
2977
2978
|
});
|
|
2978
2979
|
it('it should support enabled:false in query object syntax', async () => {
|
|
2979
2980
|
const key = queryKey();
|
|
2980
|
-
const queryFn =
|
|
2981
|
+
const queryFn = vi.fn();
|
|
2981
2982
|
queryFn.mockImplementation(() => 'data');
|
|
2982
2983
|
function Page() {
|
|
2983
2984
|
const { fetchStatus } = createQuery(() => ({
|
|
@@ -3028,7 +3029,7 @@ describe('createQuery', () => {
|
|
|
3028
3029
|
<Page />
|
|
3029
3030
|
</QueryClientProvider>));
|
|
3030
3031
|
await waitFor(() => screen.getByText('fetched data'));
|
|
3031
|
-
const setTimeoutSpy =
|
|
3032
|
+
const setTimeoutSpy = vi.spyOn(window, 'setTimeout');
|
|
3032
3033
|
result.unmount();
|
|
3033
3034
|
expect(setTimeoutSpy).not.toHaveBeenCalled();
|
|
3034
3035
|
});
|
|
@@ -3046,14 +3047,14 @@ describe('createQuery', () => {
|
|
|
3046
3047
|
<Page />
|
|
3047
3048
|
</QueryClientProvider>));
|
|
3048
3049
|
await waitFor(() => screen.getByText('fetched data'));
|
|
3049
|
-
const setTimeoutSpy =
|
|
3050
|
+
const setTimeoutSpy = vi.spyOn(window, 'setTimeout');
|
|
3050
3051
|
result.unmount();
|
|
3051
3052
|
expect(setTimeoutSpy).toHaveBeenLastCalledWith(expect.any(Function), 1000 * 60 * 10);
|
|
3052
3053
|
});
|
|
3053
3054
|
it('should not cause memo churn when data does not change', async () => {
|
|
3054
3055
|
const key = queryKey();
|
|
3055
|
-
const queryFn =
|
|
3056
|
-
const memoFn =
|
|
3056
|
+
const queryFn = vi.fn().mockReturnValue('data');
|
|
3057
|
+
const memoFn = vi.fn();
|
|
3057
3058
|
function Page() {
|
|
3058
3059
|
const result = createQuery(() => ({
|
|
3059
3060
|
queryKey: key,
|
|
@@ -3234,7 +3235,7 @@ describe('createQuery', () => {
|
|
|
3234
3235
|
});
|
|
3235
3236
|
it('should refetch if any query instance becomes enabled', async () => {
|
|
3236
3237
|
const key = queryKey();
|
|
3237
|
-
const queryFn =
|
|
3238
|
+
const queryFn = vi.fn().mockReturnValue('data');
|
|
3238
3239
|
function Disabled() {
|
|
3239
3240
|
createQuery(() => ({ queryKey: key, queryFn, enabled: false }));
|
|
3240
3241
|
return null;
|
|
@@ -3502,12 +3503,53 @@ describe('createQuery', () => {
|
|
|
3502
3503
|
// effect should not be triggered again due to structural sharing
|
|
3503
3504
|
expect(states).toHaveLength(1);
|
|
3504
3505
|
});
|
|
3506
|
+
it('The reconcile fn callback should correctly maintain referential equality', async () => {
|
|
3507
|
+
const key1 = queryKey();
|
|
3508
|
+
const states = [];
|
|
3509
|
+
function Page() {
|
|
3510
|
+
const [forceValue, setForceValue] = createSignal(1);
|
|
3511
|
+
const state = createQuery(() => ({
|
|
3512
|
+
queryKey: key1,
|
|
3513
|
+
queryFn: async () => {
|
|
3514
|
+
await sleep(10);
|
|
3515
|
+
return [1, 2];
|
|
3516
|
+
},
|
|
3517
|
+
select: (res) => res.map((x) => x + 1),
|
|
3518
|
+
reconcile(oldData, newData) {
|
|
3519
|
+
return reconcile(newData)(oldData);
|
|
3520
|
+
},
|
|
3521
|
+
}));
|
|
3522
|
+
createEffect(() => {
|
|
3523
|
+
if (state.data) {
|
|
3524
|
+
states.push(state.data);
|
|
3525
|
+
}
|
|
3526
|
+
});
|
|
3527
|
+
const forceUpdate = () => {
|
|
3528
|
+
setForceValue((prev) => prev + 1);
|
|
3529
|
+
};
|
|
3530
|
+
return (<div>
|
|
3531
|
+
<h2>Data: {JSON.stringify(state.data)}</h2>
|
|
3532
|
+
<h2>forceValue: {forceValue}</h2>
|
|
3533
|
+
<button onClick={forceUpdate}>forceUpdate</button>
|
|
3534
|
+
</div>);
|
|
3535
|
+
}
|
|
3536
|
+
render(() => (<QueryClientProvider client={queryClient}>
|
|
3537
|
+
<Page />
|
|
3538
|
+
</QueryClientProvider>));
|
|
3539
|
+
await waitFor(() => screen.getByText('Data: [2,3]'));
|
|
3540
|
+
expect(states).toHaveLength(1);
|
|
3541
|
+
fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i }));
|
|
3542
|
+
await waitFor(() => screen.getByText('forceValue: 2'));
|
|
3543
|
+
await waitFor(() => screen.getByText('Data: [2,3]'));
|
|
3544
|
+
// effect should not be triggered again due to structural sharing
|
|
3545
|
+
expect(states).toHaveLength(1);
|
|
3546
|
+
});
|
|
3505
3547
|
it('should cancel the query function when there are no more subscriptions', async () => {
|
|
3506
3548
|
const key = queryKey();
|
|
3507
|
-
let cancelFn =
|
|
3549
|
+
let cancelFn = vi.fn();
|
|
3508
3550
|
const queryFn = ({ signal }) => {
|
|
3509
3551
|
const promise = new Promise((resolve, reject) => {
|
|
3510
|
-
cancelFn =
|
|
3552
|
+
cancelFn = vi.fn(() => reject('Cancelled'));
|
|
3511
3553
|
signal?.addEventListener('abort', cancelFn);
|
|
3512
3554
|
sleep(20).then(() => resolve('OK'));
|
|
3513
3555
|
});
|
|
@@ -3767,7 +3809,7 @@ describe('createQuery', () => {
|
|
|
3767
3809
|
expect(renders).toBe(hashes);
|
|
3768
3810
|
});
|
|
3769
3811
|
it('should refetch when changed enabled to true in error state', async () => {
|
|
3770
|
-
const queryFn =
|
|
3812
|
+
const queryFn = vi.fn();
|
|
3771
3813
|
queryFn.mockImplementation(async () => {
|
|
3772
3814
|
await sleep(10);
|
|
3773
3815
|
return Promise.reject(new Error('Suspense Error Bingo'));
|
|
@@ -4514,7 +4556,7 @@ describe('createQuery', () => {
|
|
|
4514
4556
|
});
|
|
4515
4557
|
it('setQueryData - should not call onSuccess callback of active observers', async () => {
|
|
4516
4558
|
const key = queryKey();
|
|
4517
|
-
const onSuccess =
|
|
4559
|
+
const onSuccess = vi.fn();
|
|
4518
4560
|
function Page() {
|
|
4519
4561
|
const state = createQuery(() => ({
|
|
4520
4562
|
queryKey: key,
|
|
@@ -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';
|
|
@@ -141,7 +142,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
141
142
|
});
|
|
142
143
|
it('should call onSuccess on the first successful call', async () => {
|
|
143
144
|
const key = queryKey();
|
|
144
|
-
const successFn =
|
|
145
|
+
const successFn = vi.fn();
|
|
145
146
|
function Page() {
|
|
146
147
|
createQuery(() => ({
|
|
147
148
|
queryKey: [key],
|
|
@@ -166,8 +167,8 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
166
167
|
});
|
|
167
168
|
it('should call every onSuccess handler within a suspense boundary', async () => {
|
|
168
169
|
const key = queryKey();
|
|
169
|
-
const successFn1 =
|
|
170
|
-
const successFn2 =
|
|
170
|
+
const successFn1 = vi.fn();
|
|
171
|
+
const successFn2 = vi.fn();
|
|
171
172
|
function FirstComponent() {
|
|
172
173
|
createQuery(() => ({
|
|
173
174
|
queryKey: key,
|
|
@@ -514,7 +515,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
514
515
|
});
|
|
515
516
|
it('should not call the queryFn when not enabled', async () => {
|
|
516
517
|
const key = queryKey();
|
|
517
|
-
const queryFn =
|
|
518
|
+
const queryFn = vi.fn();
|
|
518
519
|
queryFn.mockImplementation(async () => {
|
|
519
520
|
await sleep(10);
|
|
520
521
|
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>);
|
|
@@ -5,6 +5,7 @@ import { createEffect, createRenderEffect, createSignal, Show } from 'solid-js';
|
|
|
5
5
|
import { render } from 'solid-testing-library';
|
|
6
6
|
import * as MutationCacheModule from '../../../query-core/src/mutationCache';
|
|
7
7
|
import { setActTimeout } from './utils';
|
|
8
|
+
import { vi } from 'vitest';
|
|
8
9
|
describe('useIsMutating', () => {
|
|
9
10
|
it('should return the number of fetching mutations', async () => {
|
|
10
11
|
const isMutatings = [];
|
|
@@ -54,9 +55,7 @@ describe('useIsMutating', () => {
|
|
|
54
55
|
const isMutatings = [];
|
|
55
56
|
const queryClient = createQueryClient();
|
|
56
57
|
function IsMutating() {
|
|
57
|
-
const isMutating = useIsMutating(() => ({
|
|
58
|
-
filters: { mutationKey: ['mutation1'] },
|
|
59
|
-
}));
|
|
58
|
+
const isMutating = useIsMutating(() => ({ mutationKey: ['mutation1'] }));
|
|
60
59
|
createRenderEffect(() => {
|
|
61
60
|
isMutatings.push(isMutating());
|
|
62
61
|
});
|
|
@@ -94,9 +93,7 @@ describe('useIsMutating', () => {
|
|
|
94
93
|
const queryClient = createQueryClient();
|
|
95
94
|
function IsMutating() {
|
|
96
95
|
const isMutating = useIsMutating(() => ({
|
|
97
|
-
|
|
98
|
-
predicate: (mutation) => mutation.options.mutationKey?.[0] === 'mutation1',
|
|
99
|
-
},
|
|
96
|
+
predicate: (mutation) => mutation.options.mutationKey?.[0] === 'mutation1',
|
|
100
97
|
}));
|
|
101
98
|
createRenderEffect(() => {
|
|
102
99
|
isMutatings.push(isMutating());
|
|
@@ -130,6 +127,27 @@ describe('useIsMutating', () => {
|
|
|
130
127
|
// Again, No unnecessary re-renders like React
|
|
131
128
|
await waitFor(() => expect(isMutatings).toEqual([0, 1, 0]));
|
|
132
129
|
});
|
|
130
|
+
it('should use provided custom queryClient', async () => {
|
|
131
|
+
const queryClient = createQueryClient();
|
|
132
|
+
function Page() {
|
|
133
|
+
const isMutating = useIsMutating(undefined, () => queryClient);
|
|
134
|
+
const { mutate } = createMutation(() => ({
|
|
135
|
+
mutationKey: ['mutation1'],
|
|
136
|
+
mutationFn: async () => {
|
|
137
|
+
await sleep(10);
|
|
138
|
+
return 'data';
|
|
139
|
+
},
|
|
140
|
+
}), () => queryClient);
|
|
141
|
+
createEffect(() => {
|
|
142
|
+
mutate();
|
|
143
|
+
});
|
|
144
|
+
return (<div>
|
|
145
|
+
<div>mutating: {isMutating}</div>
|
|
146
|
+
</div>);
|
|
147
|
+
}
|
|
148
|
+
render(() => <Page></Page>);
|
|
149
|
+
await waitFor(() => screen.findByText('mutating: 1'));
|
|
150
|
+
});
|
|
133
151
|
it('should not change state if unmounted', async () => {
|
|
134
152
|
// We have to mock the MutationCache to not unsubscribe
|
|
135
153
|
// the listener when the component is unmounted
|
|
@@ -139,7 +157,7 @@ describe('useIsMutating', () => {
|
|
|
139
157
|
return () => void 0;
|
|
140
158
|
}
|
|
141
159
|
}
|
|
142
|
-
const MutationCacheSpy =
|
|
160
|
+
const MutationCacheSpy = vi
|
|
143
161
|
.spyOn(MutationCacheModule, 'MutationCache')
|
|
144
162
|
.mockImplementation((fn) => {
|
|
145
163
|
return new MutationCacheMock(fn);
|
|
@@ -177,25 +195,4 @@ describe('useIsMutating', () => {
|
|
|
177
195
|
await sleep(20);
|
|
178
196
|
MutationCacheSpy.mockRestore();
|
|
179
197
|
});
|
|
180
|
-
it('should use provided custom queryClient', async () => {
|
|
181
|
-
const queryClient = createQueryClient();
|
|
182
|
-
function Page() {
|
|
183
|
-
const isMutating = useIsMutating(() => ({ queryClient }));
|
|
184
|
-
const { mutate } = createMutation(() => ({
|
|
185
|
-
mutationKey: ['mutation1'],
|
|
186
|
-
mutationFn: async () => {
|
|
187
|
-
await sleep(10);
|
|
188
|
-
return 'data';
|
|
189
|
-
},
|
|
190
|
-
}), () => queryClient);
|
|
191
|
-
createEffect(() => {
|
|
192
|
-
mutate();
|
|
193
|
-
});
|
|
194
|
-
return (<div>
|
|
195
|
-
<div>mutating: {isMutating}</div>
|
|
196
|
-
</div>);
|
|
197
|
-
}
|
|
198
|
-
render(() => <Page></Page>);
|
|
199
|
-
await waitFor(() => screen.findByText('mutating: 1'));
|
|
200
|
-
});
|
|
201
198
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { QueryClient } from '
|
|
1
|
+
import { QueryClient } from '../QueryClient';
|
|
2
2
|
import { createEffect, createSignal, onCleanup, Show } from 'solid-js';
|
|
3
|
+
import { vi } from 'vitest';
|
|
3
4
|
let queryKeyCount = 0;
|
|
4
5
|
export function queryKey() {
|
|
5
6
|
queryKeyCount++;
|
|
@@ -20,10 +21,10 @@ export function createQueryClient(config) {
|
|
|
20
21
|
return new QueryClient(config);
|
|
21
22
|
}
|
|
22
23
|
export function mockVisibilityState(value) {
|
|
23
|
-
return
|
|
24
|
+
return vi.spyOn(document, 'visibilityState', 'get').mockReturnValue(value);
|
|
24
25
|
}
|
|
25
26
|
export function mockNavigatorOnLine(value) {
|
|
26
|
-
return
|
|
27
|
+
return vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(value);
|
|
27
28
|
}
|
|
28
29
|
export function sleep(timeout) {
|
|
29
30
|
return new Promise((resolve, _reject) => {
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import { hydrate } from '@tanstack/query-core';
|
|
2
2
|
import { notifyManager } from '@tanstack/query-core';
|
|
3
3
|
import { isServer } from 'solid-js/web';
|
|
4
|
-
import { createComputed, createMemo, createResource, on, onCleanup,
|
|
5
|
-
import { createStore, unwrap } from 'solid-js/store';
|
|
4
|
+
import { createComputed, createMemo, createResource, on, onCleanup, } from 'solid-js';
|
|
5
|
+
import { createStore, reconcile, unwrap } from 'solid-js/store';
|
|
6
6
|
import { useQueryClient } from './QueryClientProvider';
|
|
7
7
|
import { shouldThrowError } from './utils';
|
|
8
|
+
function reconcileFn(store, result, reconcileOption) {
|
|
9
|
+
if (reconcileOption === false)
|
|
10
|
+
return result;
|
|
11
|
+
if (typeof reconcileOption === 'function') {
|
|
12
|
+
const newData = reconcileOption(store.data, result.data);
|
|
13
|
+
return { ...result, data: newData };
|
|
14
|
+
}
|
|
15
|
+
const newData = reconcile(result.data, { key: reconcileOption })(store.data);
|
|
16
|
+
return { ...result, data: newData };
|
|
17
|
+
}
|
|
8
18
|
// Base Query Function that is used to create the query.
|
|
9
19
|
export function createBaseQuery(options, Observer, queryClient) {
|
|
10
20
|
const client = createMemo(() => useQueryClient(queryClient?.()));
|
|
11
21
|
const defaultedOptions = client().defaultQueryOptions(options());
|
|
12
22
|
defaultedOptions._optimisticResults = 'optimistic';
|
|
23
|
+
defaultedOptions.structuralSharing = false;
|
|
13
24
|
if (isServer) {
|
|
14
25
|
defaultedOptions.retry = false;
|
|
15
26
|
defaultedOptions.throwErrors = true;
|
|
@@ -19,7 +30,19 @@ export function createBaseQuery(options, Observer, queryClient) {
|
|
|
19
30
|
const createServerSubscriber = (resolve, reject) => {
|
|
20
31
|
return observer.subscribe((result) => {
|
|
21
32
|
notifyManager.batchCalls(() => {
|
|
22
|
-
const
|
|
33
|
+
const query = observer.getCurrentQuery();
|
|
34
|
+
const { refetch, ...rest } = unwrap(result);
|
|
35
|
+
const unwrappedResult = {
|
|
36
|
+
...rest,
|
|
37
|
+
// hydrate() expects a QueryState object, which is similar but not
|
|
38
|
+
// quite the same as a QueryObserverResult object. Thus, for now, we're
|
|
39
|
+
// copying over the missing properties from state in order to support hydration
|
|
40
|
+
dataUpdateCount: query.state.dataUpdateCount,
|
|
41
|
+
fetchFailureCount: query.state.fetchFailureCount,
|
|
42
|
+
fetchFailureReason: query.state.fetchFailureReason,
|
|
43
|
+
fetchMeta: query.state.fetchMeta,
|
|
44
|
+
isInvalidated: query.state.isInvalidated,
|
|
45
|
+
};
|
|
23
46
|
if (unwrappedResult.isError) {
|
|
24
47
|
if (process.env['NODE_ENV'] === 'development') {
|
|
25
48
|
console.error(unwrappedResult.error);
|
|
@@ -27,6 +50,8 @@ export function createBaseQuery(options, Observer, queryClient) {
|
|
|
27
50
|
reject(unwrappedResult.error);
|
|
28
51
|
}
|
|
29
52
|
if (unwrappedResult.isSuccess) {
|
|
53
|
+
// Use of any here is fine
|
|
54
|
+
// We cannot include refetch since it is not serializable
|
|
30
55
|
resolve(unwrappedResult);
|
|
31
56
|
}
|
|
32
57
|
})();
|
|
@@ -35,17 +60,21 @@ export function createBaseQuery(options, Observer, queryClient) {
|
|
|
35
60
|
const createClientSubscriber = () => {
|
|
36
61
|
return observer.subscribe((result) => {
|
|
37
62
|
notifyManager.batchCalls(() => {
|
|
38
|
-
|
|
63
|
+
// @ts-expect-error - This will error because the reconcile option does not
|
|
64
|
+
// exist on the query-core QueryObserverResult type
|
|
65
|
+
const reconcileOptions = observer.options.reconcile;
|
|
39
66
|
// If the query has data we dont suspend but instead mutate the resource
|
|
40
67
|
// This could happen when placeholderData/initialData is defined
|
|
41
|
-
if (queryResource()?.data &&
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
68
|
+
if (queryResource()?.data && result.data && !queryResource.loading) {
|
|
69
|
+
setState((store) => {
|
|
70
|
+
return reconcileFn(store, result, reconcileOptions === undefined ? 'id' : reconcileOptions);
|
|
71
|
+
});
|
|
45
72
|
mutate(state);
|
|
46
73
|
}
|
|
47
74
|
else {
|
|
48
|
-
setState(
|
|
75
|
+
setState((store) => {
|
|
76
|
+
return reconcileFn(store, result, reconcileOptions === undefined ? 'id' : reconcileOptions);
|
|
77
|
+
});
|
|
49
78
|
refetch();
|
|
50
79
|
}
|
|
51
80
|
})();
|
|
@@ -119,12 +148,11 @@ export function createBaseQuery(options, Observer, queryClient) {
|
|
|
119
148
|
unsubscribe = null;
|
|
120
149
|
}
|
|
121
150
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
151
|
+
createComputed(on(() => client().defaultQueryOptions(options()), () => observer.setOptions(client().defaultQueryOptions(options())), {
|
|
152
|
+
// Defer because we don't need to trigger on first render
|
|
153
|
+
// This only cares about changes to options after the observer is created
|
|
154
|
+
defer: true,
|
|
155
|
+
}));
|
|
128
156
|
createComputed(on(() => state.status, () => {
|
|
129
157
|
if (state.isError &&
|
|
130
158
|
!state.isFetching &&
|
|
@@ -137,10 +165,8 @@ export function createBaseQuery(options, Observer, queryClient) {
|
|
|
137
165
|
}));
|
|
138
166
|
const handler = {
|
|
139
167
|
get(target, prop) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
return Reflect.get(target, prop);
|
|
168
|
+
const val = queryResource()?.[prop];
|
|
169
|
+
return val !== undefined ? val : Reflect.get(target, prop);
|
|
144
170
|
},
|
|
145
171
|
};
|
|
146
172
|
return new Proxy(state, handler);
|
|
@@ -2,14 +2,14 @@ import { notifyManager, QueriesObserver } from '@tanstack/query-core';
|
|
|
2
2
|
import { createComputed, onCleanup, onMount } from 'solid-js';
|
|
3
3
|
import { createStore, unwrap } from 'solid-js/store';
|
|
4
4
|
import { useQueryClient } from './QueryClientProvider';
|
|
5
|
-
export function createQueries(queriesOptions) {
|
|
6
|
-
const
|
|
5
|
+
export function createQueries(queriesOptions, queryClient) {
|
|
6
|
+
const client = useQueryClient(queryClient?.());
|
|
7
7
|
const defaultedQueries = queriesOptions().queries.map((options) => {
|
|
8
|
-
const defaultedOptions =
|
|
8
|
+
const defaultedOptions = client.defaultQueryOptions(options);
|
|
9
9
|
defaultedOptions._optimisticResults = 'optimistic';
|
|
10
10
|
return defaultedOptions;
|
|
11
11
|
});
|
|
12
|
-
const observer = new QueriesObserver(
|
|
12
|
+
const observer = new QueriesObserver(client, defaultedQueries);
|
|
13
13
|
const [state, setState] = createStore(observer.getOptimisticResult(defaultedQueries));
|
|
14
14
|
const unsubscribe = observer.subscribe((result) => {
|
|
15
15
|
notifyManager.batchCalls(() => {
|
|
@@ -22,7 +22,7 @@ export function createQueries(queriesOptions) {
|
|
|
22
22
|
});
|
|
23
23
|
createComputed(() => {
|
|
24
24
|
const updatedQueries = queriesOptions().queries.map((options) => {
|
|
25
|
-
const defaultedOptions =
|
|
25
|
+
const defaultedOptions = client.defaultQueryOptions(options);
|
|
26
26
|
defaultedOptions._optimisticResults = 'optimistic';
|
|
27
27
|
return defaultedOptions;
|
|
28
28
|
});
|
package/build/source/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import './setBatchUpdatesFn';
|
|
|
5
5
|
export * from '@tanstack/query-core';
|
|
6
6
|
// Solid Query
|
|
7
7
|
export * from './types';
|
|
8
|
+
export { QueryClient } from './QueryClient';
|
|
8
9
|
export { createQuery } from './createQuery';
|
|
9
10
|
export { QueryClientContext, QueryClientProvider, useQueryClient, } from './QueryClientProvider';
|
|
10
11
|
export { useIsFetching } from './useIsFetching';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createMemo, createSignal, onCleanup } from 'solid-js';
|
|
2
2
|
import { useQueryClient } from './QueryClientProvider';
|
|
3
|
-
export function useIsFetching(
|
|
4
|
-
const
|
|
5
|
-
const queryCache = createMemo(() =>
|
|
6
|
-
const [fetches, setFetches] = createSignal(
|
|
3
|
+
export function useIsFetching(filters, queryClient) {
|
|
4
|
+
const client = createMemo(() => useQueryClient(queryClient?.()));
|
|
5
|
+
const queryCache = createMemo(() => client().getQueryCache());
|
|
6
|
+
const [fetches, setFetches] = createSignal(client().isFetching(filters?.()));
|
|
7
7
|
const unsubscribe = queryCache().subscribe(() => {
|
|
8
|
-
setFetches(
|
|
8
|
+
setFetches(client().isFetching(filters?.()));
|
|
9
9
|
});
|
|
10
10
|
onCleanup(unsubscribe);
|
|
11
11
|
return fetches;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useQueryClient } from './QueryClientProvider';
|
|
2
2
|
import { createSignal, onCleanup, createMemo } from 'solid-js';
|
|
3
|
-
export function useIsMutating(
|
|
4
|
-
const
|
|
5
|
-
const mutationCache = createMemo(() =>
|
|
6
|
-
const [mutations, setMutations] = createSignal(
|
|
3
|
+
export function useIsMutating(filters, queryClient) {
|
|
4
|
+
const client = createMemo(() => useQueryClient(queryClient?.()));
|
|
5
|
+
const mutationCache = createMemo(() => client().getMutationCache());
|
|
6
|
+
const [mutations, setMutations] = createSignal(client().isMutating(filters?.()));
|
|
7
7
|
const unsubscribe = mutationCache().subscribe((_result) => {
|
|
8
|
-
setMutations(
|
|
8
|
+
setMutations(client().isMutating(filters?.()));
|
|
9
9
|
});
|
|
10
10
|
onCleanup(unsubscribe);
|
|
11
11
|
return mutations;
|