@tanstack/react-query 5.0.0-alpha.19 → 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.
@@ -190,88 +190,6 @@ describe("useQuery's in Suspense mode", () => {
190
190
  expect(queryCache.find({ queryKey: key })?.getObserversCount()).toBe(0)
191
191
  })
192
192
 
193
- it('should call onSuccess on the first successful call', async () => {
194
- const key = queryKey()
195
-
196
- const successFn = vi.fn()
197
-
198
- function Page() {
199
- useQuery({
200
- queryKey: [key],
201
- queryFn: async () => {
202
- await sleep(10)
203
- return key
204
- },
205
- suspense: true,
206
- select: () => 'selected',
207
- onSuccess: successFn,
208
- })
209
-
210
- return <>rendered</>
211
- }
212
-
213
- const rendered = renderWithClient(
214
- queryClient,
215
- <React.Suspense fallback="loading">
216
- <Page />
217
- </React.Suspense>,
218
- )
219
-
220
- await waitFor(() => rendered.getByText('rendered'))
221
-
222
- await waitFor(() => expect(successFn).toHaveBeenCalledTimes(1))
223
- await waitFor(() => expect(successFn).toHaveBeenCalledWith('selected'))
224
- })
225
-
226
- it('should call every onSuccess handler within a suspense boundary', async () => {
227
- const key = queryKey()
228
-
229
- const successFn1 = vi.fn()
230
- const successFn2 = vi.fn()
231
-
232
- function FirstComponent() {
233
- useQuery({
234
- queryKey: key,
235
- queryFn: () => {
236
- sleep(10)
237
- return 'data'
238
- },
239
- suspense: true,
240
- onSuccess: successFn1,
241
- })
242
-
243
- return <span>first</span>
244
- }
245
-
246
- function SecondComponent() {
247
- useQuery({
248
- queryKey: key,
249
- queryFn: () => {
250
- sleep(10)
251
- return 'data'
252
- },
253
-
254
- suspense: true,
255
- onSuccess: successFn2,
256
- })
257
-
258
- return <span>second</span>
259
- }
260
-
261
- const rendered = renderWithClient(
262
- queryClient,
263
- <React.Suspense fallback="loading">
264
- <FirstComponent />
265
- <SecondComponent />
266
- </React.Suspense>,
267
- )
268
-
269
- await waitFor(() => rendered.getByText('second'))
270
-
271
- await waitFor(() => expect(successFn1).toHaveBeenCalledTimes(1))
272
- await waitFor(() => expect(successFn2).toHaveBeenCalledTimes(1))
273
- })
274
-
275
193
  // https://github.com/tannerlinsley/react-query/issues/468
276
194
  it('should reset error state if new component instances are mounted', async () => {
277
195
  const consoleMock = vi
@@ -128,33 +128,6 @@ describe('select', () => {
128
128
  return result
129
129
  })
130
130
  })
131
- it('should pass transformed data to onSuccess', () => {
132
- doNotExecute(() => {
133
- const infiniteQuery = useInfiniteQuery({
134
- queryKey: ['key'],
135
- queryFn: ({ pageParam }) => {
136
- return pageParam * 5
137
- },
138
- defaultPageParam: 1,
139
- getNextPageParam: () => undefined,
140
- select: (data) => {
141
- return {
142
- ...data,
143
- pages: data.pages.map((page) => page.toString()),
144
- }
145
- },
146
- onSuccess: (data) => {
147
- const result: Expect<Equal<InfiniteData<string>, typeof data>> = true
148
- doNotExecute(() => result)
149
- },
150
- })
151
-
152
- const result: Expect<
153
- Equal<InfiniteData<string> | undefined, (typeof infiniteQuery)['data']>
154
- > = true
155
- return result
156
- })
157
- })
158
131
  })
159
132
  describe('getNextPageParam / getPreviousPageParam', () => {
160
133
  it('should get typed params', () => {
@@ -144,10 +144,6 @@ describe('useQueries', () => {
144
144
  expectTypeNotAny(a)
145
145
  return a.toLowerCase()
146
146
  },
147
- onSuccess: (a) => {
148
- expectType<string>(a)
149
- expectTypeNotAny(a)
150
- },
151
147
  placeholderData: 'string',
152
148
  // @ts-expect-error (initialData: string)
153
149
  initialData: 123,
@@ -160,14 +156,6 @@ describe('useQueries', () => {
160
156
  expectTypeNotAny(a)
161
157
  return parseInt(a)
162
158
  },
163
- onSuccess: (a) => {
164
- expectType<number>(a)
165
- expectTypeNotAny(a)
166
- },
167
- onError: (e) => {
168
- expectType<boolean>(e)
169
- expectTypeNotAny(e)
170
- },
171
159
  placeholderData: 'string',
172
160
  // @ts-expect-error (initialData: string)
173
161
  initialData: 123,
@@ -304,10 +292,6 @@ describe('useQueries', () => {
304
292
  expectTypeNotAny(a)
305
293
  return a.toLowerCase()
306
294
  },
307
- onSuccess: (a) => {
308
- expectType<string>(a)
309
- expectTypeNotAny(a)
310
- },
311
295
  placeholderData: 'string',
312
296
  // @ts-expect-error (initialData: string)
313
297
  initialData: 123,
@@ -320,14 +304,6 @@ describe('useQueries', () => {
320
304
  expectTypeNotAny(a)
321
305
  return parseInt(a)
322
306
  },
323
- onSuccess: (a) => {
324
- expectType<number>(a)
325
- expectTypeNotAny(a)
326
- },
327
- onError: (e) => {
328
- expectType<boolean>(e)
329
- expectTypeNotAny(e)
330
- },
331
307
  placeholderData: 'string',
332
308
  // @ts-expect-error (initialData: string)
333
309
  initialData: 123,
@@ -423,60 +399,38 @@ describe('useQueries', () => {
423
399
  ],
424
400
  })
425
401
 
426
- // select / onSuccess / onSettled params are "indirectly" enforced
402
+ // select params are "indirectly" enforced
427
403
  useQueries({
428
404
  queries: [
429
405
  // unfortunately TS will not suggest the type for you
430
406
  {
431
407
  queryKey: key1,
432
408
  queryFn: () => 'string',
433
- // @ts-expect-error (noImplicitAny)
434
- onSuccess: (a) => null,
435
- // @ts-expect-error (noImplicitAny)
436
- onSettled: (a) => null,
437
409
  },
438
410
  // however you can add a type to the callback
439
411
  {
440
412
  queryKey: key2,
441
413
  queryFn: () => 'string',
442
- onSuccess: (a: string) => {
443
- expectType<string>(a)
444
- expectTypeNotAny(a)
445
- },
446
- onSettled: (a: string | undefined) => {
447
- expectType<string | undefined>(a)
448
- expectTypeNotAny(a)
449
- },
450
414
  },
451
415
  // the type you do pass is enforced
452
416
  {
453
417
  queryKey: key3,
454
418
  queryFn: () => 'string',
455
- // @ts-expect-error (only accepts string)
456
- onSuccess: (a: number) => null,
457
419
  },
458
420
  {
459
421
  queryKey: key4,
460
422
  queryFn: () => 'string',
461
423
  select: (a: string) => parseInt(a),
462
- // @ts-expect-error (select is defined => only accepts number)
463
- onSuccess: (a: string) => null,
464
- onSettled: (a: number | undefined) => {
465
- expectType<number | undefined>(a)
466
- expectTypeNotAny(a)
467
- },
468
424
  },
469
425
  ],
470
426
  })
471
427
 
472
428
  // callbacks are also indirectly enforced with Array.map
473
429
  useQueries({
474
- // @ts-expect-error (onSuccess only accepts string)
475
430
  queries: Array(50).map((_, i) => ({
476
431
  queryKey: ['key', i] as const,
477
432
  queryFn: () => i + 10,
478
433
  select: (data: number) => data.toString(),
479
- onSuccess: (_data: number) => null,
480
434
  })),
481
435
  })
482
436
  useQueries({
@@ -484,7 +438,6 @@ describe('useQueries', () => {
484
438
  queryKey: ['key', i] as const,
485
439
  queryFn: () => i + 10,
486
440
  select: (data: number) => data.toString(),
487
- onSuccess: (_data: string) => null,
488
441
  })),
489
442
  })
490
443
 
@@ -494,32 +447,15 @@ describe('useQueries', () => {
494
447
  {
495
448
  queryKey: key1,
496
449
  queryFn: () => 'string',
497
- // @ts-expect-error (noImplicitAny)
498
- onSuccess: (a) => null,
499
- // @ts-expect-error (noImplicitAny)
500
- onSettled: (a) => null,
501
450
  },
502
451
  {
503
452
  queryKey: key2,
504
453
  queryFn: () => 'string',
505
- onSuccess: (a: string) => {
506
- expectType<string>(a)
507
- expectTypeNotAny(a)
508
- },
509
- onSettled: (a: string | undefined) => {
510
- expectType<string | undefined>(a)
511
- expectTypeNotAny(a)
512
- },
513
454
  },
514
455
  {
515
456
  queryKey: key4,
516
457
  queryFn: () => 'string',
517
458
  select: (a: string) => parseInt(a),
518
- onSuccess: (_a: number) => null,
519
- onSettled: (a: number | undefined) => {
520
- expectType<number | undefined>(a)
521
- expectTypeNotAny(a)
522
- },
523
459
  },
524
460
  ],
525
461
  })
@@ -533,12 +469,6 @@ describe('useQueries', () => {
533
469
  {
534
470
  queryKey: key1,
535
471
  queryFn: () => Promise.resolve('string'),
536
- onSuccess: (a: string) => {
537
- expectType<string>(a)
538
- expectTypeNotAny(a)
539
- },
540
- // @ts-expect-error (refuses to accept a Promise)
541
- onSettled: (a: Promise<string>) => null,
542
472
  },
543
473
  ],
544
474
  })
@@ -645,11 +575,10 @@ describe('useQueries', () => {
645
575
  queries: queries.map(
646
576
  // no need to type the mapped query
647
577
  (query) => {
648
- const { queryFn: fn, queryKey: key, onError: err } = query
578
+ const { queryFn: fn, queryKey: key } = query
649
579
  expectType<QueryFunction<TQueryFnData, TQueryKey> | undefined>(fn)
650
580
  return {
651
581
  queryKey: key,
652
- onError: err,
653
582
  queryFn: fn
654
583
  ? (ctx: QueryFunctionContext<TQueryKey>) => {
655
584
  expectType<TQueryKey>(ctx.queryKey)
@@ -64,23 +64,17 @@ describe('useQuery', () => {
64
64
  useQuery({
65
65
  queryKey: [key],
66
66
  queryFn: async () => true,
67
- onSuccess: (data) => expectType<boolean>(data),
68
- onSettled: (data) => expectType<boolean | undefined>(data),
69
67
  })
70
68
 
71
69
  // it should be possible to specify a union type as result type
72
70
  const unionTypeSync = useQuery({
73
71
  queryKey: key,
74
72
  queryFn: () => (Math.random() > 0.5 ? 'a' : 'b'),
75
-
76
- onSuccess: (data) => expectType<'a' | 'b'>(data),
77
73
  })
78
74
  expectType<'a' | 'b' | undefined>(unionTypeSync.data)
79
75
  const unionTypeAsync = useQuery<'a' | 'b'>({
80
76
  queryKey: key,
81
77
  queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'),
82
-
83
- onSuccess: (data) => expectType<'a' | 'b'>(data),
84
78
  })
85
79
  expectType<'a' | 'b' | undefined>(unionTypeAsync.data)
86
80
 
@@ -450,255 +444,6 @@ describe('useQuery', () => {
450
444
  })
451
445
  })
452
446
 
453
- it('should call onSuccess after a query has been fetched', async () => {
454
- const key = queryKey()
455
- const states: UseQueryResult<string>[] = []
456
- const onSuccess = vi.fn()
457
-
458
- function Page() {
459
- const state = useQuery({
460
- queryKey: key,
461
- queryFn: async () => {
462
- await sleep(10)
463
- return 'data'
464
- },
465
- onSuccess,
466
- })
467
- states.push(state)
468
- return <div>data: {state.data}</div>
469
- }
470
-
471
- const rendered = renderWithClient(queryClient, <Page />)
472
-
473
- await rendered.findByText('data: data')
474
- expect(states.length).toBe(2)
475
- expect(onSuccess).toHaveBeenCalledTimes(1)
476
- expect(onSuccess).toHaveBeenCalledWith('data')
477
- })
478
-
479
- it('should call onSuccess after a query has been refetched', async () => {
480
- const key = queryKey()
481
- const states: UseQueryResult<string>[] = []
482
- const onSuccess = vi.fn()
483
- let count = 0
484
-
485
- function Page() {
486
- const state = useQuery({
487
- queryKey: key,
488
- queryFn: async () => {
489
- count++
490
- await sleep(10)
491
- return 'data' + count
492
- },
493
- onSuccess,
494
- })
495
-
496
- states.push(state)
497
-
498
- return (
499
- <div>
500
- <div>data: {state.data}</div>
501
- <button onClick={() => state.refetch()}>refetch</button>
502
- </div>
503
- )
504
- }
505
-
506
- const rendered = renderWithClient(queryClient, <Page />)
507
-
508
- await rendered.findByText('data: data1')
509
- fireEvent.click(rendered.getByRole('button', { name: /refetch/i }))
510
- await rendered.findByText('data: data2')
511
-
512
- expect(states.length).toBe(3) //pending, success, success after refetch
513
- expect(count).toBe(2)
514
- expect(onSuccess).toHaveBeenCalledTimes(2)
515
- })
516
-
517
- it('should call onSuccess after a disabled query has been fetched', async () => {
518
- const key = queryKey()
519
- const states: UseQueryResult<string>[] = []
520
- const onSuccess = vi.fn()
521
-
522
- function Page() {
523
- const state = useQuery({
524
- queryKey: key,
525
- queryFn: () => 'data',
526
- enabled: false,
527
- onSuccess,
528
- })
529
-
530
- states.push(state)
531
-
532
- return (
533
- <div>
534
- <div>isSuccess: {state.isSuccess ? 'true' : 'false'}</div>
535
- <button onClick={() => state.refetch()}>refetch</button>
536
- </div>
537
- )
538
- }
539
-
540
- const rendered = renderWithClient(queryClient, <Page />)
541
-
542
- await waitFor(() => {
543
- rendered.getByText('isSuccess: false')
544
- })
545
-
546
- fireEvent.click(rendered.getByRole('button', { name: 'refetch' }))
547
-
548
- await waitFor(() => {
549
- rendered.getByText('isSuccess: true')
550
- })
551
-
552
- expect(onSuccess).toHaveBeenCalledTimes(1)
553
- expect(onSuccess).toHaveBeenCalledWith('data')
554
- })
555
-
556
- it('should not call onSuccess if a component has unmounted', async () => {
557
- const key = queryKey()
558
- const states: UseQueryResult<string>[] = []
559
- const onSuccess = vi.fn()
560
-
561
- function Page() {
562
- const [show, setShow] = React.useState(true)
563
-
564
- React.useEffect(() => {
565
- setShow(false)
566
- }, [setShow])
567
-
568
- return show ? <Component /> : null
569
- }
570
-
571
- function Component() {
572
- const state = useQuery({
573
- queryKey: key,
574
- queryFn: async () => {
575
- await sleep(10)
576
- return 'data'
577
- },
578
- onSuccess,
579
- })
580
- states.push(state)
581
- return null
582
- }
583
-
584
- renderWithClient(queryClient, <Page />)
585
-
586
- await sleep(50)
587
- expect(states.length).toBe(1)
588
- expect(onSuccess).toHaveBeenCalledTimes(0)
589
- })
590
-
591
- it('should call onError after a query has been fetched with an error', async () => {
592
- const key = queryKey()
593
- const states: UseQueryResult<unknown>[] = []
594
- const onError = vi.fn()
595
-
596
- function Page() {
597
- const state = useQuery<unknown>({
598
- queryKey: key,
599
- queryFn: () => Promise.reject(new Error('error')),
600
- retry: false,
601
- onError,
602
- })
603
- states.push(state)
604
- return null
605
- }
606
-
607
- renderWithClient(queryClient, <Page />)
608
-
609
- await sleep(10)
610
- expect(states.length).toBe(2)
611
- expect(onError).toHaveBeenCalledTimes(1)
612
- expect(onError).toHaveBeenCalledWith(new Error('error'))
613
- })
614
-
615
- it('should not call onError when receiving a CancelledError', async () => {
616
- const key = queryKey()
617
- const onError = vi.fn()
618
-
619
- function Page() {
620
- const { status, fetchStatus } = useQuery({
621
- queryKey: key,
622
- queryFn: async () => {
623
- await sleep(10)
624
- return 23
625
- },
626
-
627
- onError,
628
- })
629
- return (
630
- <span>
631
- status: {status}, fetchStatus: {fetchStatus}
632
- </span>
633
- )
634
- }
635
-
636
- const rendered = renderWithClient(queryClient, <Page />)
637
-
638
- rendered.getByText('status: pending, fetchStatus: fetching')
639
-
640
- await queryClient.cancelQueries({ queryKey: key })
641
- // query cancellation will reset the query to it's initial state
642
- await waitFor(() =>
643
- rendered.getByText('status: pending, fetchStatus: idle'),
644
- )
645
- expect(onError).not.toHaveBeenCalled()
646
- })
647
-
648
- it('should call onSettled after a query has been fetched', async () => {
649
- const key = queryKey()
650
- const states: UseQueryResult<string>[] = []
651
- const onSettled = vi.fn()
652
-
653
- function Page() {
654
- const state = useQuery({
655
- queryKey: key,
656
- queryFn: () => 'data',
657
- onSettled,
658
- })
659
- states.push(state)
660
-
661
- return <div>data: {state.data}</div>
662
- }
663
-
664
- const rendered = renderWithClient(queryClient, <Page />)
665
-
666
- await waitFor(() => {
667
- rendered.getByText('data: data')
668
- })
669
-
670
- expect(states.length).toBe(2)
671
- expect(onSettled).toHaveBeenCalledTimes(1)
672
- expect(onSettled).toHaveBeenCalledWith('data', null)
673
- })
674
-
675
- it('should call onSettled after a query has been fetched with an error', async () => {
676
- const key = queryKey()
677
- const onSettled = vi.fn()
678
- const error = new Error('error')
679
-
680
- function Page() {
681
- const state = useQuery({
682
- queryKey: key,
683
- queryFn: async () => {
684
- await sleep(10)
685
- return Promise.reject(error)
686
- },
687
- retry: false,
688
- onSettled,
689
- })
690
- return <div>status: {state.status}</div>
691
- }
692
-
693
- const rendered = renderWithClient(queryClient, <Page />)
694
-
695
- await waitFor(() => {
696
- rendered.getByText('status: error')
697
- })
698
- expect(onSettled).toHaveBeenCalledTimes(1)
699
- expect(onSettled).toHaveBeenCalledWith(undefined, error)
700
- })
701
-
702
447
  it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => {
703
448
  const key = queryKey()
704
449
  let fetchCount = 0
@@ -2587,53 +2332,6 @@ describe('useQuery', () => {
2587
2332
  expect(renders).toBe(2)
2588
2333
  })
2589
2334
 
2590
- it('should batch re-renders including hook callbacks', async () => {
2591
- const key = queryKey()
2592
-
2593
- let renders = 0
2594
- let callbackCount = 0
2595
-
2596
- const queryFn = async () => {
2597
- await sleep(10)
2598
- return 'data'
2599
- }
2600
-
2601
- function Page() {
2602
- const [count, setCount] = React.useState(0)
2603
- useQuery({
2604
- queryKey: key,
2605
- queryFn,
2606
- onSuccess: () => {
2607
- setCount((x) => x + 1)
2608
- },
2609
- })
2610
- useQuery({
2611
- queryKey: key,
2612
- queryFn,
2613
- onSuccess: () => {
2614
- setCount((x) => x + 1)
2615
- },
2616
- })
2617
-
2618
- React.useEffect(() => {
2619
- renders++
2620
- callbackCount = count
2621
- })
2622
-
2623
- return <div>count: {count}</div>
2624
- }
2625
-
2626
- const rendered = renderWithClient(queryClient, <Page />)
2627
-
2628
- await waitFor(() => rendered.getByText('count: 2'))
2629
-
2630
- // Should be 3 instead of 5
2631
- expect(renders).toBe(3)
2632
-
2633
- // Both callbacks should have been executed
2634
- expect(callbackCount).toBe(2)
2635
- })
2636
-
2637
2335
  it('should render latest data even if react has discarded certain renders', async () => {
2638
2336
  const key = queryKey()
2639
2337
 
@@ -6126,36 +5824,6 @@ describe('useQuery', () => {
6126
5824
  })
6127
5825
  })
6128
5826
 
6129
- it('setQueryData - should not call onSuccess callback of active observers', async () => {
6130
- const key = queryKey()
6131
- const onSuccess = vi.fn()
6132
-
6133
- function Page() {
6134
- const state = useQuery({
6135
- queryKey: key,
6136
- queryFn: () => 'data',
6137
- onSuccess,
6138
- })
6139
- return (
6140
- <div>
6141
- <div>data: {state.data}</div>
6142
- <button onClick={() => queryClient.setQueryData(key, 'newData')}>
6143
- setQueryData
6144
- </button>
6145
- </div>
6146
- )
6147
- }
6148
-
6149
- const rendered = renderWithClient(queryClient, <Page />)
6150
-
6151
- await waitFor(() => rendered.getByText('data: data'))
6152
- fireEvent.click(rendered.getByRole('button', { name: /setQueryData/i }))
6153
- await waitFor(() => rendered.getByText('data: newData'))
6154
-
6155
- expect(onSuccess).toHaveBeenCalledTimes(1)
6156
- expect(onSuccess).toHaveBeenCalledWith('data')
6157
- })
6158
-
6159
5827
  it('setQueryData - should respect updatedAt', async () => {
6160
5828
  const key = queryKey()
6161
5829
 
package/src/suspense.ts CHANGED
@@ -46,14 +46,6 @@ export const fetchOptimistic = <
46
46
  observer: QueryObserver<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
47
47
  errorResetBoundary: QueryErrorResetBoundaryValue,
48
48
  ) =>
49
- observer
50
- .fetchOptimistic(defaultedOptions)
51
- .then(({ data }) => {
52
- defaultedOptions.onSuccess?.(data as TData)
53
- defaultedOptions.onSettled?.(data, null)
54
- })
55
- .catch((error) => {
56
- errorResetBoundary.clearReset()
57
- defaultedOptions.onError?.(error)
58
- defaultedOptions.onSettled?.(undefined, error)
59
- })
49
+ observer.fetchOptimistic(defaultedOptions).catch(() => {
50
+ errorResetBoundary.clearReset()
51
+ })