@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
|
@@ -33,6 +33,9 @@ import {
|
|
|
33
33
|
setActTimeout,
|
|
34
34
|
sleep,
|
|
35
35
|
} from './utils'
|
|
36
|
+
import { vi } from 'vitest'
|
|
37
|
+
import type { Mock } from 'vitest'
|
|
38
|
+
import { reconcile } from 'solid-js/store'
|
|
36
39
|
|
|
37
40
|
describe('createQuery', () => {
|
|
38
41
|
const queryCache = new QueryCache()
|
|
@@ -491,7 +494,7 @@ describe('createQuery', () => {
|
|
|
491
494
|
it('should call onSuccess after a query has been fetched', async () => {
|
|
492
495
|
const key = queryKey()
|
|
493
496
|
const states: CreateQueryResult<string>[] = []
|
|
494
|
-
const onSuccess =
|
|
497
|
+
const onSuccess = vi.fn()
|
|
495
498
|
|
|
496
499
|
function Page() {
|
|
497
500
|
const state = createQuery(() => ({
|
|
@@ -523,7 +526,7 @@ describe('createQuery', () => {
|
|
|
523
526
|
it('should call onSuccess after a disabled query has been fetched', async () => {
|
|
524
527
|
const key = queryKey()
|
|
525
528
|
const states: CreateQueryResult<string>[] = []
|
|
526
|
-
const onSuccess =
|
|
529
|
+
const onSuccess = vi.fn()
|
|
527
530
|
|
|
528
531
|
function Page() {
|
|
529
532
|
const state = createQuery(() => ({
|
|
@@ -561,7 +564,7 @@ describe('createQuery', () => {
|
|
|
561
564
|
it('should not call onSuccess if a component has unmounted', async () => {
|
|
562
565
|
const key = queryKey()
|
|
563
566
|
const states: CreateQueryResult<string>[] = []
|
|
564
|
-
const onSuccess =
|
|
567
|
+
const onSuccess = vi.fn()
|
|
565
568
|
|
|
566
569
|
function Page() {
|
|
567
570
|
const [show, setShow] = createSignal(true)
|
|
@@ -601,7 +604,7 @@ describe('createQuery', () => {
|
|
|
601
604
|
it('should call onError after a query has been fetched with an error', async () => {
|
|
602
605
|
const key = queryKey()
|
|
603
606
|
const states: CreateQueryResult<unknown>[] = []
|
|
604
|
-
const onError =
|
|
607
|
+
const onError = vi.fn()
|
|
605
608
|
|
|
606
609
|
function Page() {
|
|
607
610
|
const state = createQuery(() => ({
|
|
@@ -632,7 +635,7 @@ describe('createQuery', () => {
|
|
|
632
635
|
|
|
633
636
|
it('should not call onError when receiving a CancelledError', async () => {
|
|
634
637
|
const key = queryKey()
|
|
635
|
-
const onError =
|
|
638
|
+
const onError = vi.fn()
|
|
636
639
|
|
|
637
640
|
function Page() {
|
|
638
641
|
const state = createQuery(() => ({
|
|
@@ -666,7 +669,7 @@ describe('createQuery', () => {
|
|
|
666
669
|
it('should call onSettled after a query has been fetched', async () => {
|
|
667
670
|
const key = queryKey()
|
|
668
671
|
const states: CreateQueryResult<string>[] = []
|
|
669
|
-
const onSettled =
|
|
672
|
+
const onSettled = vi.fn()
|
|
670
673
|
|
|
671
674
|
function Page() {
|
|
672
675
|
const state = createQuery(() => ({
|
|
@@ -696,7 +699,7 @@ describe('createQuery', () => {
|
|
|
696
699
|
it('should call onSettled after a query has been fetched with an error', async () => {
|
|
697
700
|
const key = queryKey()
|
|
698
701
|
const states: CreateQueryResult<string>[] = []
|
|
699
|
-
const onSettled =
|
|
702
|
+
const onSettled = vi.fn()
|
|
700
703
|
|
|
701
704
|
function Page() {
|
|
702
705
|
const state = createQuery(() => ({
|
|
@@ -1280,7 +1283,6 @@ describe('createQuery', () => {
|
|
|
1280
1283
|
count++
|
|
1281
1284
|
return count === 1 ? result1 : result2
|
|
1282
1285
|
},
|
|
1283
|
-
notifyOnChangeProps: 'all',
|
|
1284
1286
|
}))
|
|
1285
1287
|
|
|
1286
1288
|
createRenderEffect(() => {
|
|
@@ -1320,9 +1322,8 @@ describe('createQuery', () => {
|
|
|
1320
1322
|
|
|
1321
1323
|
expect(todos).toEqual(result1)
|
|
1322
1324
|
expect(newTodos).toEqual(result2)
|
|
1323
|
-
expect(newTodos).not.toBe(todos)
|
|
1324
1325
|
expect(newTodo1).toBe(todo1)
|
|
1325
|
-
expect(newTodo2).
|
|
1326
|
+
expect(newTodo2).toBe(todo2)
|
|
1326
1327
|
|
|
1327
1328
|
return null
|
|
1328
1329
|
})
|
|
@@ -2452,7 +2453,7 @@ describe('createQuery', () => {
|
|
|
2452
2453
|
|
|
2453
2454
|
it('should not refetch query on focus when `enabled` is set to `false`', async () => {
|
|
2454
2455
|
const key = queryKey()
|
|
2455
|
-
const queryFn =
|
|
2456
|
+
const queryFn = vi.fn<unknown[], string>().mockReturnValue('data')
|
|
2456
2457
|
|
|
2457
2458
|
function Page() {
|
|
2458
2459
|
const { data = 'default' } = createQuery(() => ({
|
|
@@ -3255,7 +3256,7 @@ describe('createQuery', () => {
|
|
|
3255
3256
|
|
|
3256
3257
|
it('should keep initial data when the query key changes', async () => {
|
|
3257
3258
|
const key = queryKey()
|
|
3258
|
-
const states: DefinedCreateQueryResult<{ count: number }
|
|
3259
|
+
const states: Partial<DefinedCreateQueryResult<{ count: number }>>[] = []
|
|
3259
3260
|
|
|
3260
3261
|
function Page() {
|
|
3261
3262
|
const [count, setCount] = createSignal(0)
|
|
@@ -3264,6 +3265,7 @@ describe('createQuery', () => {
|
|
|
3264
3265
|
queryFn: () => ({ count: 10 }),
|
|
3265
3266
|
staleTime: Infinity,
|
|
3266
3267
|
initialData: () => ({ count: count() }),
|
|
3268
|
+
reconcile: false,
|
|
3267
3269
|
}))
|
|
3268
3270
|
createRenderEffect(() => {
|
|
3269
3271
|
states.push({ ...state })
|
|
@@ -3296,7 +3298,7 @@ describe('createQuery', () => {
|
|
|
3296
3298
|
it('should retry specified number of times', async () => {
|
|
3297
3299
|
const key = queryKey()
|
|
3298
3300
|
|
|
3299
|
-
const queryFn =
|
|
3301
|
+
const queryFn = vi.fn<unknown[], unknown>()
|
|
3300
3302
|
queryFn.mockImplementation(() => {
|
|
3301
3303
|
return Promise.reject(new Error('Error test Barrett'))
|
|
3302
3304
|
})
|
|
@@ -3337,7 +3339,7 @@ describe('createQuery', () => {
|
|
|
3337
3339
|
it('should not retry if retry function `false`', async () => {
|
|
3338
3340
|
const key = queryKey()
|
|
3339
3341
|
|
|
3340
|
-
const queryFn =
|
|
3342
|
+
const queryFn = vi.fn<unknown[], unknown>()
|
|
3341
3343
|
|
|
3342
3344
|
queryFn.mockImplementationOnce(() => {
|
|
3343
3345
|
return Promise.reject(new Error('Error test Tanner'))
|
|
@@ -3385,7 +3387,7 @@ describe('createQuery', () => {
|
|
|
3385
3387
|
|
|
3386
3388
|
type DelayError = { delay: number }
|
|
3387
3389
|
|
|
3388
|
-
const queryFn =
|
|
3390
|
+
const queryFn = vi.fn<unknown[], unknown>()
|
|
3389
3391
|
queryFn.mockImplementation(() => {
|
|
3390
3392
|
return Promise.reject({ delay: 50 })
|
|
3391
3393
|
})
|
|
@@ -3596,10 +3598,10 @@ describe('createQuery', () => {
|
|
|
3596
3598
|
const key = queryKey()
|
|
3597
3599
|
const states: CreateQueryResult<string>[] = []
|
|
3598
3600
|
|
|
3599
|
-
const queryFn =
|
|
3601
|
+
const queryFn = vi.fn<unknown[], string>()
|
|
3600
3602
|
queryFn.mockImplementation(() => 'data')
|
|
3601
3603
|
|
|
3602
|
-
const prefetchQueryFn =
|
|
3604
|
+
const prefetchQueryFn = vi.fn<unknown[], string>()
|
|
3603
3605
|
prefetchQueryFn.mockImplementation(() => 'not yet...')
|
|
3604
3606
|
|
|
3605
3607
|
await queryClient.prefetchQuery({
|
|
@@ -3633,10 +3635,10 @@ describe('createQuery', () => {
|
|
|
3633
3635
|
it('should not refetch if not stale after a prefetch', async () => {
|
|
3634
3636
|
const key = queryKey()
|
|
3635
3637
|
|
|
3636
|
-
const queryFn =
|
|
3638
|
+
const queryFn = vi.fn<unknown[], string>()
|
|
3637
3639
|
queryFn.mockImplementation(() => 'data')
|
|
3638
3640
|
|
|
3639
|
-
const prefetchQueryFn =
|
|
3641
|
+
const prefetchQueryFn = vi.fn<unknown[], Promise<string>>()
|
|
3640
3642
|
prefetchQueryFn.mockImplementation(async () => {
|
|
3641
3643
|
await sleep(10)
|
|
3642
3644
|
return 'not yet...'
|
|
@@ -3909,7 +3911,7 @@ describe('createQuery', () => {
|
|
|
3909
3911
|
|
|
3910
3912
|
it('it should support enabled:false in query object syntax', async () => {
|
|
3911
3913
|
const key = queryKey()
|
|
3912
|
-
const queryFn =
|
|
3914
|
+
const queryFn = vi.fn<unknown[], string>()
|
|
3913
3915
|
queryFn.mockImplementation(() => 'data')
|
|
3914
3916
|
|
|
3915
3917
|
function Page() {
|
|
@@ -3981,7 +3983,7 @@ describe('createQuery', () => {
|
|
|
3981
3983
|
))
|
|
3982
3984
|
|
|
3983
3985
|
await waitFor(() => screen.getByText('fetched data'))
|
|
3984
|
-
const setTimeoutSpy =
|
|
3986
|
+
const setTimeoutSpy = vi.spyOn(window, 'setTimeout')
|
|
3985
3987
|
|
|
3986
3988
|
result.unmount()
|
|
3987
3989
|
|
|
@@ -4007,7 +4009,7 @@ describe('createQuery', () => {
|
|
|
4007
4009
|
))
|
|
4008
4010
|
|
|
4009
4011
|
await waitFor(() => screen.getByText('fetched data'))
|
|
4010
|
-
const setTimeoutSpy =
|
|
4012
|
+
const setTimeoutSpy = vi.spyOn(window, 'setTimeout')
|
|
4011
4013
|
|
|
4012
4014
|
result.unmount()
|
|
4013
4015
|
|
|
@@ -4019,8 +4021,8 @@ describe('createQuery', () => {
|
|
|
4019
4021
|
|
|
4020
4022
|
it('should not cause memo churn when data does not change', async () => {
|
|
4021
4023
|
const key = queryKey()
|
|
4022
|
-
const queryFn =
|
|
4023
|
-
const memoFn =
|
|
4024
|
+
const queryFn = vi.fn<unknown[], string>().mockReturnValue('data')
|
|
4025
|
+
const memoFn = vi.fn()
|
|
4024
4026
|
|
|
4025
4027
|
function Page() {
|
|
4026
4028
|
const result = createQuery(() => ({
|
|
@@ -4255,7 +4257,7 @@ describe('createQuery', () => {
|
|
|
4255
4257
|
it('should refetch if any query instance becomes enabled', async () => {
|
|
4256
4258
|
const key = queryKey()
|
|
4257
4259
|
|
|
4258
|
-
const queryFn =
|
|
4260
|
+
const queryFn = vi.fn<unknown[], string>().mockReturnValue('data')
|
|
4259
4261
|
|
|
4260
4262
|
function Disabled() {
|
|
4261
4263
|
createQuery(() => ({ queryKey: key, queryFn, enabled: false }))
|
|
@@ -4608,13 +4610,68 @@ describe('createQuery', () => {
|
|
|
4608
4610
|
expect(states).toHaveLength(1)
|
|
4609
4611
|
})
|
|
4610
4612
|
|
|
4613
|
+
it('The reconcile fn callback should correctly maintain referential equality', async () => {
|
|
4614
|
+
const key1 = queryKey()
|
|
4615
|
+
const states: Array<Array<number>> = []
|
|
4616
|
+
|
|
4617
|
+
function Page() {
|
|
4618
|
+
const [forceValue, setForceValue] = createSignal(1)
|
|
4619
|
+
|
|
4620
|
+
const state = createQuery(() => ({
|
|
4621
|
+
queryKey: key1,
|
|
4622
|
+
queryFn: async () => {
|
|
4623
|
+
await sleep(10)
|
|
4624
|
+
return [1, 2]
|
|
4625
|
+
},
|
|
4626
|
+
select: (res) => res.map((x) => x + 1),
|
|
4627
|
+
reconcile(oldData, newData) {
|
|
4628
|
+
return reconcile(newData)(oldData)
|
|
4629
|
+
},
|
|
4630
|
+
}))
|
|
4631
|
+
|
|
4632
|
+
createEffect(() => {
|
|
4633
|
+
if (state.data) {
|
|
4634
|
+
states.push(state.data)
|
|
4635
|
+
}
|
|
4636
|
+
})
|
|
4637
|
+
|
|
4638
|
+
const forceUpdate = () => {
|
|
4639
|
+
setForceValue((prev) => prev + 1)
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4642
|
+
return (
|
|
4643
|
+
<div>
|
|
4644
|
+
<h2>Data: {JSON.stringify(state.data)}</h2>
|
|
4645
|
+
<h2>forceValue: {forceValue}</h2>
|
|
4646
|
+
<button onClick={forceUpdate}>forceUpdate</button>
|
|
4647
|
+
</div>
|
|
4648
|
+
)
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
render(() => (
|
|
4652
|
+
<QueryClientProvider client={queryClient}>
|
|
4653
|
+
<Page />
|
|
4654
|
+
</QueryClientProvider>
|
|
4655
|
+
))
|
|
4656
|
+
await waitFor(() => screen.getByText('Data: [2,3]'))
|
|
4657
|
+
expect(states).toHaveLength(1)
|
|
4658
|
+
|
|
4659
|
+
fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i }))
|
|
4660
|
+
|
|
4661
|
+
await waitFor(() => screen.getByText('forceValue: 2'))
|
|
4662
|
+
await waitFor(() => screen.getByText('Data: [2,3]'))
|
|
4663
|
+
|
|
4664
|
+
// effect should not be triggered again due to structural sharing
|
|
4665
|
+
expect(states).toHaveLength(1)
|
|
4666
|
+
})
|
|
4667
|
+
|
|
4611
4668
|
it('should cancel the query function when there are no more subscriptions', async () => {
|
|
4612
4669
|
const key = queryKey()
|
|
4613
|
-
let cancelFn:
|
|
4670
|
+
let cancelFn: Mock = vi.fn()
|
|
4614
4671
|
|
|
4615
4672
|
const queryFn = ({ signal }: { signal?: AbortSignal }) => {
|
|
4616
4673
|
const promise = new Promise<string>((resolve, reject) => {
|
|
4617
|
-
cancelFn =
|
|
4674
|
+
cancelFn = vi.fn(() => reject('Cancelled'))
|
|
4618
4675
|
signal?.addEventListener('abort', cancelFn)
|
|
4619
4676
|
sleep(20).then(() => resolve('OK'))
|
|
4620
4677
|
})
|
|
@@ -4958,7 +5015,7 @@ describe('createQuery', () => {
|
|
|
4958
5015
|
})
|
|
4959
5016
|
|
|
4960
5017
|
it('should refetch when changed enabled to true in error state', async () => {
|
|
4961
|
-
const queryFn =
|
|
5018
|
+
const queryFn = vi.fn<unknown[], unknown>()
|
|
4962
5019
|
queryFn.mockImplementation(async () => {
|
|
4963
5020
|
await sleep(10)
|
|
4964
5021
|
return Promise.reject(new Error('Suspense Error Bingo'))
|
|
@@ -6052,7 +6109,7 @@ describe('createQuery', () => {
|
|
|
6052
6109
|
|
|
6053
6110
|
it('setQueryData - should not call onSuccess callback of active observers', async () => {
|
|
6054
6111
|
const key = queryKey()
|
|
6055
|
-
const onSuccess =
|
|
6112
|
+
const onSuccess = vi.fn()
|
|
6056
6113
|
|
|
6057
6114
|
function Page() {
|
|
6058
6115
|
const state = createQuery(() => ({
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
QueryClientProvider,
|
|
21
21
|
} from '..'
|
|
22
22
|
import { createQueryClient, queryKey, sleep } from './utils'
|
|
23
|
+
import { vi } from 'vitest'
|
|
23
24
|
|
|
24
25
|
describe("useQuery's in Suspense mode", () => {
|
|
25
26
|
const queryCache = new QueryCache()
|
|
@@ -142,7 +143,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
142
143
|
it('should not call the queryFn twice when used in Suspense mode', async () => {
|
|
143
144
|
const key = queryKey()
|
|
144
145
|
|
|
145
|
-
const queryFn =
|
|
146
|
+
const queryFn = vi.fn<unknown[], string>()
|
|
146
147
|
queryFn.mockImplementation(() => {
|
|
147
148
|
sleep(10)
|
|
148
149
|
return 'data'
|
|
@@ -219,7 +220,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
219
220
|
it('should call onSuccess on the first successful call', async () => {
|
|
220
221
|
const key = queryKey()
|
|
221
222
|
|
|
222
|
-
const successFn =
|
|
223
|
+
const successFn = vi.fn()
|
|
223
224
|
|
|
224
225
|
function Page() {
|
|
225
226
|
createQuery(() => ({
|
|
@@ -254,8 +255,8 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
254
255
|
it('should call every onSuccess handler within a suspense boundary', async () => {
|
|
255
256
|
const key = queryKey()
|
|
256
257
|
|
|
257
|
-
const successFn1 =
|
|
258
|
-
const successFn2 =
|
|
258
|
+
const successFn1 = vi.fn()
|
|
259
|
+
const successFn2 = vi.fn()
|
|
259
260
|
|
|
260
261
|
function FirstComponent() {
|
|
261
262
|
createQuery(() => ({
|
|
@@ -733,7 +734,7 @@ describe("useQuery's in Suspense mode", () => {
|
|
|
733
734
|
it('should not call the queryFn when not enabled', async () => {
|
|
734
735
|
const key = queryKey()
|
|
735
736
|
|
|
736
|
-
const queryFn =
|
|
737
|
+
const queryFn = vi.fn<unknown[], Promise<string>>()
|
|
737
738
|
queryFn.mockImplementation(async () => {
|
|
738
739
|
await sleep(10)
|
|
739
740
|
return '23'
|
|
@@ -152,9 +152,7 @@ describe('useIsFetching', () => {
|
|
|
152
152
|
function Page() {
|
|
153
153
|
const [started, setStarted] = createSignal(false)
|
|
154
154
|
const isFetching = useIsFetching(() => ({
|
|
155
|
-
|
|
156
|
-
queryKey: key1,
|
|
157
|
-
},
|
|
155
|
+
queryKey: key1,
|
|
158
156
|
}))
|
|
159
157
|
|
|
160
158
|
createRenderEffect(() => {
|
|
@@ -237,7 +235,7 @@ describe('useIsFetching', () => {
|
|
|
237
235
|
() => queryClient,
|
|
238
236
|
)
|
|
239
237
|
|
|
240
|
-
const isFetching = useIsFetching(() =>
|
|
238
|
+
const isFetching = useIsFetching(undefined, () => queryClient)
|
|
241
239
|
|
|
242
240
|
return (
|
|
243
241
|
<div>
|
|
@@ -6,6 +6,7 @@ import { createEffect, createRenderEffect, createSignal, Show } from 'solid-js'
|
|
|
6
6
|
import { render } from 'solid-testing-library'
|
|
7
7
|
import * as MutationCacheModule from '../../../query-core/src/mutationCache'
|
|
8
8
|
import { setActTimeout } from './utils'
|
|
9
|
+
import { vi } from 'vitest'
|
|
9
10
|
|
|
10
11
|
describe('useIsMutating', () => {
|
|
11
12
|
it('should return the number of fetching mutations', async () => {
|
|
@@ -68,9 +69,7 @@ describe('useIsMutating', () => {
|
|
|
68
69
|
const queryClient = createQueryClient()
|
|
69
70
|
|
|
70
71
|
function IsMutating() {
|
|
71
|
-
const isMutating = useIsMutating(() => ({
|
|
72
|
-
filters: { mutationKey: ['mutation1'] },
|
|
73
|
-
}))
|
|
72
|
+
const isMutating = useIsMutating(() => ({ mutationKey: ['mutation1'] }))
|
|
74
73
|
createRenderEffect(() => {
|
|
75
74
|
isMutatings.push(isMutating())
|
|
76
75
|
})
|
|
@@ -116,10 +115,8 @@ describe('useIsMutating', () => {
|
|
|
116
115
|
|
|
117
116
|
function IsMutating() {
|
|
118
117
|
const isMutating = useIsMutating(() => ({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
mutation.options.mutationKey?.[0] === 'mutation1',
|
|
122
|
-
},
|
|
118
|
+
predicate: (mutation) =>
|
|
119
|
+
mutation.options.mutationKey?.[0] === 'mutation1',
|
|
123
120
|
}))
|
|
124
121
|
createRenderEffect(() => {
|
|
125
122
|
isMutatings.push(isMutating())
|
|
@@ -161,6 +158,33 @@ describe('useIsMutating', () => {
|
|
|
161
158
|
await waitFor(() => expect(isMutatings).toEqual([0, 1, 0]))
|
|
162
159
|
})
|
|
163
160
|
|
|
161
|
+
it('should use provided custom queryClient', async () => {
|
|
162
|
+
const queryClient = createQueryClient()
|
|
163
|
+
function Page() {
|
|
164
|
+
const isMutating = useIsMutating(undefined, () => queryClient)
|
|
165
|
+
const { mutate } = createMutation(
|
|
166
|
+
() => ({
|
|
167
|
+
mutationKey: ['mutation1'],
|
|
168
|
+
mutationFn: async () => {
|
|
169
|
+
await sleep(10)
|
|
170
|
+
return 'data'
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
() => queryClient,
|
|
174
|
+
)
|
|
175
|
+
createEffect(() => {
|
|
176
|
+
mutate()
|
|
177
|
+
})
|
|
178
|
+
return (
|
|
179
|
+
<div>
|
|
180
|
+
<div>mutating: {isMutating}</div>
|
|
181
|
+
</div>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
render(() => <Page></Page>)
|
|
185
|
+
await waitFor(() => screen.findByText('mutating: 1'))
|
|
186
|
+
})
|
|
187
|
+
|
|
164
188
|
it('should not change state if unmounted', async () => {
|
|
165
189
|
// We have to mock the MutationCache to not unsubscribe
|
|
166
190
|
// the listener when the component is unmounted
|
|
@@ -171,7 +195,7 @@ describe('useIsMutating', () => {
|
|
|
171
195
|
}
|
|
172
196
|
}
|
|
173
197
|
|
|
174
|
-
const MutationCacheSpy =
|
|
198
|
+
const MutationCacheSpy = vi
|
|
175
199
|
.spyOn(MutationCacheModule, 'MutationCache')
|
|
176
200
|
.mockImplementation((fn) => {
|
|
177
201
|
return new MutationCacheMock(fn)
|
|
@@ -221,36 +245,4 @@ describe('useIsMutating', () => {
|
|
|
221
245
|
await sleep(20)
|
|
222
246
|
MutationCacheSpy.mockRestore()
|
|
223
247
|
})
|
|
224
|
-
|
|
225
|
-
it('should use provided custom queryClient', async () => {
|
|
226
|
-
const queryClient = createQueryClient()
|
|
227
|
-
|
|
228
|
-
function Page() {
|
|
229
|
-
const isMutating = useIsMutating(() => ({ queryClient }))
|
|
230
|
-
const { mutate } = createMutation(
|
|
231
|
-
() => ({
|
|
232
|
-
mutationKey: ['mutation1'],
|
|
233
|
-
mutationFn: async () => {
|
|
234
|
-
await sleep(10)
|
|
235
|
-
return 'data'
|
|
236
|
-
},
|
|
237
|
-
}),
|
|
238
|
-
() => queryClient,
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
createEffect(() => {
|
|
242
|
-
mutate()
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
return (
|
|
246
|
-
<div>
|
|
247
|
-
<div>mutating: {isMutating}</div>
|
|
248
|
-
</div>
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
render(() => <Page></Page>)
|
|
253
|
-
|
|
254
|
-
await waitFor(() => screen.findByText('mutating: 1'))
|
|
255
|
-
})
|
|
256
248
|
})
|
package/src/__tests__/utils.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { QueryClientConfig } from '@tanstack/query-core'
|
|
2
|
-
import { QueryClient } from '
|
|
2
|
+
import { QueryClient } from '../QueryClient'
|
|
3
3
|
import type { ParentProps } from 'solid-js'
|
|
4
4
|
import { createEffect, createSignal, onCleanup, Show } from 'solid-js'
|
|
5
|
+
import { vi } from 'vitest'
|
|
5
6
|
|
|
6
7
|
let queryKeyCount = 0
|
|
7
8
|
export function queryKey(): Array<string> {
|
|
@@ -34,11 +35,11 @@ export function createQueryClient(config?: QueryClientConfig): QueryClient {
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export function mockVisibilityState(value: DocumentVisibilityState) {
|
|
37
|
-
return
|
|
38
|
+
return vi.spyOn(document, 'visibilityState', 'get').mockReturnValue(value)
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
export function mockNavigatorOnLine(value: boolean) {
|
|
41
|
-
return
|
|
42
|
+
return vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(value)
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export function sleep(timeout: number): Promise<void> {
|
package/src/createBaseQuery.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// in solid-js/web package. I'll create a GitHub issue with them to see
|
|
4
4
|
// why that happens.
|
|
5
5
|
import type {
|
|
6
|
-
QueryClient,
|
|
7
6
|
QueryKey,
|
|
8
7
|
QueryObserver,
|
|
9
8
|
QueryObserverResult,
|
|
10
9
|
} from '@tanstack/query-core'
|
|
10
|
+
import type { QueryClient } from './QueryClient'
|
|
11
11
|
import { hydrate } from '@tanstack/query-core'
|
|
12
12
|
import { notifyManager } from '@tanstack/query-core'
|
|
13
13
|
import type { Accessor } from 'solid-js'
|
|
@@ -18,13 +18,29 @@ import {
|
|
|
18
18
|
createResource,
|
|
19
19
|
on,
|
|
20
20
|
onCleanup,
|
|
21
|
-
onMount,
|
|
22
21
|
} from 'solid-js'
|
|
23
|
-
import { createStore, unwrap } from 'solid-js/store'
|
|
22
|
+
import { createStore, reconcile, unwrap } from 'solid-js/store'
|
|
24
23
|
import { useQueryClient } from './QueryClientProvider'
|
|
25
24
|
import type { CreateBaseQueryOptions } from './types'
|
|
26
25
|
import { shouldThrowError } from './utils'
|
|
27
26
|
|
|
27
|
+
function reconcileFn<TData, TError>(
|
|
28
|
+
store: QueryObserverResult<TData, TError>,
|
|
29
|
+
result: QueryObserverResult<TData, TError>,
|
|
30
|
+
reconcileOption:
|
|
31
|
+
| string
|
|
32
|
+
| false
|
|
33
|
+
| ((oldData: TData | undefined, newData: TData) => TData),
|
|
34
|
+
): QueryObserverResult<TData, TError> {
|
|
35
|
+
if (reconcileOption === false) return result
|
|
36
|
+
if (typeof reconcileOption === 'function') {
|
|
37
|
+
const newData = reconcileOption(store.data, result.data as TData)
|
|
38
|
+
return { ...result, data: newData } as typeof result
|
|
39
|
+
}
|
|
40
|
+
const newData = reconcile(result.data, { key: reconcileOption })(store.data)
|
|
41
|
+
return { ...result, data: newData } as typeof result
|
|
42
|
+
}
|
|
43
|
+
|
|
28
44
|
// Base Query Function that is used to create the query.
|
|
29
45
|
export function createBaseQuery<
|
|
30
46
|
TQueryFnData,
|
|
@@ -37,12 +53,13 @@ export function createBaseQuery<
|
|
|
37
53
|
CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
38
54
|
>,
|
|
39
55
|
Observer: typeof QueryObserver,
|
|
40
|
-
queryClient?:
|
|
56
|
+
queryClient?: Accessor<QueryClient>,
|
|
41
57
|
) {
|
|
42
58
|
const client = createMemo(() => useQueryClient(queryClient?.()))
|
|
43
59
|
|
|
44
60
|
const defaultedOptions = client().defaultQueryOptions(options())
|
|
45
61
|
defaultedOptions._optimisticResults = 'optimistic'
|
|
62
|
+
defaultedOptions.structuralSharing = false
|
|
46
63
|
if (isServer) {
|
|
47
64
|
defaultedOptions.retry = false
|
|
48
65
|
defaultedOptions.throwErrors = true
|
|
@@ -64,7 +81,21 @@ export function createBaseQuery<
|
|
|
64
81
|
) => {
|
|
65
82
|
return observer.subscribe((result) => {
|
|
66
83
|
notifyManager.batchCalls(() => {
|
|
67
|
-
const
|
|
84
|
+
const query = observer.getCurrentQuery()
|
|
85
|
+
const { refetch, ...rest } = unwrap(result)
|
|
86
|
+
const unwrappedResult = {
|
|
87
|
+
...rest,
|
|
88
|
+
|
|
89
|
+
// hydrate() expects a QueryState object, which is similar but not
|
|
90
|
+
// quite the same as a QueryObserverResult object. Thus, for now, we're
|
|
91
|
+
// copying over the missing properties from state in order to support hydration
|
|
92
|
+
dataUpdateCount: query.state.dataUpdateCount,
|
|
93
|
+
fetchFailureCount: query.state.fetchFailureCount,
|
|
94
|
+
fetchFailureReason: query.state.fetchFailureReason,
|
|
95
|
+
fetchMeta: query.state.fetchMeta,
|
|
96
|
+
isInvalidated: query.state.isInvalidated,
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
if (unwrappedResult.isError) {
|
|
69
100
|
if (process.env['NODE_ENV'] === 'development') {
|
|
70
101
|
console.error(unwrappedResult.error)
|
|
@@ -72,7 +103,9 @@ export function createBaseQuery<
|
|
|
72
103
|
reject(unwrappedResult.error)
|
|
73
104
|
}
|
|
74
105
|
if (unwrappedResult.isSuccess) {
|
|
75
|
-
|
|
106
|
+
// Use of any here is fine
|
|
107
|
+
// We cannot include refetch since it is not serializable
|
|
108
|
+
resolve(unwrappedResult as any)
|
|
76
109
|
}
|
|
77
110
|
})()
|
|
78
111
|
})
|
|
@@ -81,18 +114,28 @@ export function createBaseQuery<
|
|
|
81
114
|
const createClientSubscriber = () => {
|
|
82
115
|
return observer.subscribe((result) => {
|
|
83
116
|
notifyManager.batchCalls(() => {
|
|
84
|
-
|
|
117
|
+
// @ts-expect-error - This will error because the reconcile option does not
|
|
118
|
+
// exist on the query-core QueryObserverResult type
|
|
119
|
+
const reconcileOptions = observer.options.reconcile
|
|
85
120
|
// If the query has data we dont suspend but instead mutate the resource
|
|
86
121
|
// This could happen when placeholderData/initialData is defined
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
122
|
+
if (queryResource()?.data && result.data && !queryResource.loading) {
|
|
123
|
+
setState((store) => {
|
|
124
|
+
return reconcileFn(
|
|
125
|
+
store,
|
|
126
|
+
result,
|
|
127
|
+
reconcileOptions === undefined ? 'id' : reconcileOptions,
|
|
128
|
+
)
|
|
129
|
+
})
|
|
93
130
|
mutate(state)
|
|
94
131
|
} else {
|
|
95
|
-
setState(
|
|
132
|
+
setState((store) => {
|
|
133
|
+
return reconcileFn(
|
|
134
|
+
store,
|
|
135
|
+
result,
|
|
136
|
+
reconcileOptions === undefined ? 'id' : reconcileOptions,
|
|
137
|
+
)
|
|
138
|
+
})
|
|
96
139
|
refetch()
|
|
97
140
|
}
|
|
98
141
|
})()
|
|
@@ -178,13 +221,17 @@ export function createBaseQuery<
|
|
|
178
221
|
}
|
|
179
222
|
})
|
|
180
223
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
224
|
+
createComputed(
|
|
225
|
+
on(
|
|
226
|
+
() => client().defaultQueryOptions(options()),
|
|
227
|
+
() => observer.setOptions(client().defaultQueryOptions(options())),
|
|
228
|
+
{
|
|
229
|
+
// Defer because we don't need to trigger on first render
|
|
230
|
+
// This only cares about changes to options after the observer is created
|
|
231
|
+
defer: true,
|
|
232
|
+
},
|
|
233
|
+
),
|
|
234
|
+
)
|
|
188
235
|
|
|
189
236
|
createComputed(
|
|
190
237
|
on(
|
|
@@ -209,10 +256,8 @@ export function createBaseQuery<
|
|
|
209
256
|
target: QueryObserverResult<TData, TError>,
|
|
210
257
|
prop: keyof QueryObserverResult<TData, TError>,
|
|
211
258
|
): any {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
return Reflect.get(target, prop)
|
|
259
|
+
const val = queryResource()?.[prop]
|
|
260
|
+
return val !== undefined ? val : Reflect.get(target, prop)
|
|
216
261
|
},
|
|
217
262
|
}
|
|
218
263
|
|