@tanstack/react-query 4.12.0 → 4.13.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-query",
3
- "version": "4.12.0",
3
+ "version": "4.13.0",
4
4
  "description": "Hooks for managing, caching and syncing asynchronous and remote data in React",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "react-error-boundary": "^3.1.4"
47
47
  },
48
48
  "dependencies": {
49
- "@tanstack/query-core": "4.12.0",
49
+ "@tanstack/query-core": "4.13.0",
50
50
  "use-sync-external-store": "^1.2.0"
51
51
  },
52
52
  "peerDependencies": {
@@ -71,6 +71,7 @@ describe('useInfiniteQuery', () => {
71
71
  error: null,
72
72
  errorUpdatedAt: 0,
73
73
  failureCount: 0,
74
+ failureReason: null,
74
75
  errorUpdateCount: 0,
75
76
  fetchNextPage: expect.any(Function),
76
77
  fetchPreviousPage: expect.any(Function),
@@ -104,6 +105,7 @@ describe('useInfiniteQuery', () => {
104
105
  error: null,
105
106
  errorUpdatedAt: 0,
106
107
  failureCount: 0,
108
+ failureReason: null,
107
109
  errorUpdateCount: 0,
108
110
  fetchNextPage: expect.any(Function),
109
111
  fetchPreviousPage: expect.any(Function),
@@ -147,6 +147,57 @@ describe('useMutation', () => {
147
147
  expect(onSettledMock).toHaveBeenCalledWith(3)
148
148
  })
149
149
 
150
+ it('should set correct values for `failureReason` and `failureCount` on multiple mutate calls', async () => {
151
+ let count = 0
152
+ type Value = { count: number }
153
+
154
+ const mutateFn = jest.fn<Promise<Value>, [value: Value]>()
155
+
156
+ mutateFn.mockImplementationOnce(() => {
157
+ return Promise.reject('Error test Jonas')
158
+ })
159
+
160
+ mutateFn.mockImplementation(async (value) => {
161
+ await sleep(10)
162
+ return Promise.resolve(value)
163
+ })
164
+
165
+ function Page() {
166
+ const { mutate, failureCount, failureReason, data, status } = useMutation<
167
+ Value,
168
+ string,
169
+ Value
170
+ >(mutateFn)
171
+
172
+ return (
173
+ <div>
174
+ <h1>Data {data?.count}</h1>
175
+ <h2>Status {status}</h2>
176
+ <h2>Failed {failureCount} times</h2>
177
+ <h2>Failed because {failureReason ?? 'null'}</h2>
178
+ <button onClick={() => mutate({ count: ++count })}>mutate</button>
179
+ </div>
180
+ )
181
+ }
182
+
183
+ const rendered = renderWithClient(queryClient, <Page />)
184
+
185
+ await waitFor(() => rendered.getByText('Data'))
186
+
187
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
188
+ await waitFor(() => rendered.getByText('Data'))
189
+ await waitFor(() => rendered.getByText('Status error'))
190
+ await waitFor(() => rendered.getByText('Failed 1 times'))
191
+ await waitFor(() => rendered.getByText('Failed because Error test Jonas'))
192
+
193
+ fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
194
+ await waitFor(() => rendered.getByText('Status loading'))
195
+ await waitFor(() => rendered.getByText('Status success'))
196
+ await waitFor(() => rendered.getByText('Data 2'))
197
+ await waitFor(() => rendered.getByText('Failed 0 times'))
198
+ await waitFor(() => rendered.getByText('Failed because null'))
199
+ })
200
+
150
201
  it('should be able to call `onError` and `onSettled` after each failed mutate', async () => {
151
202
  const onErrorMock = jest.fn()
152
203
  const onSettledMock = jest.fn()
@@ -581,21 +632,25 @@ describe('useMutation', () => {
581
632
  isLoading: false,
582
633
  isPaused: false,
583
634
  failureCount: 0,
635
+ failureReason: null,
584
636
  })
585
637
  expect(states[1]).toMatchObject({
586
638
  isLoading: true,
587
639
  isPaused: false,
588
640
  failureCount: 0,
641
+ failureReason: null,
589
642
  })
590
643
  expect(states[2]).toMatchObject({
591
644
  isLoading: true,
592
645
  isPaused: false,
593
646
  failureCount: 1,
647
+ failureReason: 'oops',
594
648
  })
595
649
  expect(states[3]).toMatchObject({
596
650
  isLoading: true,
597
651
  isPaused: true,
598
652
  failureCount: 1,
653
+ failureReason: 'oops',
599
654
  })
600
655
 
601
656
  onlineMock.mockReturnValue(true)
@@ -608,11 +663,13 @@ describe('useMutation', () => {
608
663
  isLoading: true,
609
664
  isPaused: false,
610
665
  failureCount: 1,
666
+ failureReason: 'oops',
611
667
  })
612
668
  expect(states[5]).toMatchObject({
613
669
  isLoading: false,
614
670
  isPaused: false,
615
- failureCount: 1,
671
+ failureCount: 0,
672
+ failureReason: null,
616
673
  data: 'data',
617
674
  })
618
675
 
@@ -233,6 +233,7 @@ describe('useQuery', () => {
233
233
  error: null,
234
234
  errorUpdatedAt: 0,
235
235
  failureCount: 0,
236
+ failureReason: null,
236
237
  errorUpdateCount: 0,
237
238
  isError: false,
238
239
  isFetched: false,
@@ -260,6 +261,7 @@ describe('useQuery', () => {
260
261
  error: null,
261
262
  errorUpdatedAt: 0,
262
263
  failureCount: 0,
264
+ failureReason: null,
263
265
  errorUpdateCount: 0,
264
266
  isError: false,
265
267
  isFetched: true,
@@ -303,6 +305,7 @@ describe('useQuery', () => {
303
305
  <div>
304
306
  <h1>Status: {state.status}</h1>
305
307
  <div>Failure Count: {state.failureCount}</div>
308
+ <div>Failure Reason: {state.failureReason}</div>
306
309
  </div>
307
310
  )
308
311
  }
@@ -317,6 +320,7 @@ describe('useQuery', () => {
317
320
  error: null,
318
321
  errorUpdatedAt: 0,
319
322
  failureCount: 0,
323
+ failureReason: null,
320
324
  errorUpdateCount: 0,
321
325
  isError: false,
322
326
  isFetched: false,
@@ -344,6 +348,7 @@ describe('useQuery', () => {
344
348
  error: null,
345
349
  errorUpdatedAt: 0,
346
350
  failureCount: 1,
351
+ failureReason: 'rejected',
347
352
  errorUpdateCount: 0,
348
353
  isError: false,
349
354
  isFetched: false,
@@ -371,6 +376,7 @@ describe('useQuery', () => {
371
376
  error: 'rejected',
372
377
  errorUpdatedAt: expect.any(Number),
373
378
  failureCount: 2,
379
+ failureReason: 'rejected',
374
380
  errorUpdateCount: 1,
375
381
  isError: true,
376
382
  isFetched: true,
@@ -2877,6 +2883,7 @@ describe('useQuery', () => {
2877
2883
  <div>
2878
2884
  <div>error: {result.error ?? 'null'}</div>
2879
2885
  <div>failureCount: {result.failureCount}</div>
2886
+ <div>failureReason: {result.failureReason}</div>
2880
2887
  </div>
2881
2888
  )
2882
2889
  }
@@ -2895,6 +2902,7 @@ describe('useQuery', () => {
2895
2902
  const rendered = renderWithClient(queryClient, <App />)
2896
2903
 
2897
2904
  await waitFor(() => rendered.getByText('failureCount: 1'))
2905
+ await waitFor(() => rendered.getByText('failureReason: some error'))
2898
2906
  fireEvent.click(rendered.getByRole('button', { name: /hide/i }))
2899
2907
  await waitFor(() => rendered.getByRole('button', { name: /show/i }))
2900
2908
  fireEvent.click(rendered.getByRole('button', { name: /show/i }))
@@ -2925,6 +2933,7 @@ describe('useQuery', () => {
2925
2933
  <div>
2926
2934
  <div>error: {result.error ?? 'null'}</div>
2927
2935
  <div>failureCount: {result.failureCount}</div>
2936
+ <div>failureReason: {result.failureReason}</div>
2928
2937
  </div>
2929
2938
  )
2930
2939
  }
@@ -2946,6 +2955,7 @@ describe('useQuery', () => {
2946
2955
  const rendered = renderWithClient(queryClient, <App />)
2947
2956
 
2948
2957
  await waitFor(() => rendered.getByText('failureCount: 1'))
2958
+ await waitFor(() => rendered.getByText('failureReason: some error'))
2949
2959
  fireEvent.click(rendered.getByRole('button', { name: /hide/i }))
2950
2960
  fireEvent.click(rendered.getByRole('button', { name: /cancel/i }))
2951
2961
  await waitFor(() => rendered.getByRole('button', { name: /show/i }))
@@ -3169,15 +3179,20 @@ describe('useQuery', () => {
3169
3179
  })
3170
3180
 
3171
3181
  function Page() {
3172
- const { status, failureCount } = useQuery(key, queryFn, {
3173
- retry: 1,
3174
- retryDelay: 1,
3175
- })
3182
+ const { status, failureCount, failureReason } = useQuery<unknown, string>(
3183
+ key,
3184
+ queryFn,
3185
+ {
3186
+ retry: 1,
3187
+ retryDelay: 1,
3188
+ },
3189
+ )
3176
3190
 
3177
3191
  return (
3178
3192
  <div>
3179
3193
  <h1>{status}</h1>
3180
3194
  <h2>Failed {failureCount} times</h2>
3195
+ <h2>Failed because {failureReason}</h2>
3181
3196
  </div>
3182
3197
  )
3183
3198
  }
@@ -3189,6 +3204,7 @@ describe('useQuery', () => {
3189
3204
 
3190
3205
  // query should fail `retry + 1` times, since first time isn't a "retry"
3191
3206
  await waitFor(() => rendered.getByText('Failed 2 times'))
3207
+ await waitFor(() => rendered.getByText('Failed because Error test Barrett'))
3192
3208
 
3193
3209
  expect(queryFn).toHaveBeenCalledTimes(2)
3194
3210
  })
@@ -3207,7 +3223,7 @@ describe('useQuery', () => {
3207
3223
  })
3208
3224
 
3209
3225
  function Page() {
3210
- const { status, failureCount, error } = useQuery<
3226
+ const { status, failureCount, failureReason, error } = useQuery<
3211
3227
  unknown,
3212
3228
  string,
3213
3229
  [string]
@@ -3220,6 +3236,7 @@ describe('useQuery', () => {
3220
3236
  <div>
3221
3237
  <h1>{status}</h1>
3222
3238
  <h2>Failed {failureCount} times</h2>
3239
+ <h2>Failed because {failureReason}</h2>
3223
3240
  <h2>{error}</h2>
3224
3241
  </div>
3225
3242
  )
@@ -3231,6 +3248,8 @@ describe('useQuery', () => {
3231
3248
  await waitFor(() => rendered.getByText('error'))
3232
3249
 
3233
3250
  await waitFor(() => rendered.getByText('Failed 2 times'))
3251
+ await waitFor(() => rendered.getByText('Failed because NoRetry'))
3252
+
3234
3253
  await waitFor(() => rendered.getByText('NoRetry'))
3235
3254
 
3236
3255
  expect(queryFn).toHaveBeenCalledTimes(2)
@@ -3247,7 +3266,7 @@ describe('useQuery', () => {
3247
3266
  })
3248
3267
 
3249
3268
  function Page() {
3250
- const { status, failureCount } = useQuery(key, queryFn, {
3269
+ const { status, failureCount, failureReason } = useQuery(key, queryFn, {
3251
3270
  retry: 1,
3252
3271
  retryDelay: (_, error: DelayError) => error.delay,
3253
3272
  })
@@ -3256,6 +3275,7 @@ describe('useQuery', () => {
3256
3275
  <div>
3257
3276
  <h1>{status}</h1>
3258
3277
  <h2>Failed {failureCount} times</h2>
3278
+ <h2>Failed because DelayError: {failureReason?.delay}ms</h2>
3259
3279
  </div>
3260
3280
  )
3261
3281
  }
@@ -3266,6 +3286,7 @@ describe('useQuery', () => {
3266
3286
 
3267
3287
  expect(queryFn).toHaveBeenCalledTimes(1)
3268
3288
 
3289
+ await waitFor(() => rendered.getByText('Failed because DelayError: 50ms'))
3269
3290
  await waitFor(() => rendered.getByText('Failed 2 times'))
3270
3291
 
3271
3292
  expect(queryFn).toHaveBeenCalledTimes(2)
@@ -3281,7 +3302,7 @@ describe('useQuery', () => {
3281
3302
  let count = 0
3282
3303
 
3283
3304
  function Page() {
3284
- const query = useQuery(
3305
+ const query = useQuery<unknown, string>(
3285
3306
  key,
3286
3307
  () => {
3287
3308
  count++
@@ -3298,6 +3319,7 @@ describe('useQuery', () => {
3298
3319
  <div>error {String(query.error)}</div>
3299
3320
  <div>status {query.status}</div>
3300
3321
  <div>failureCount {query.failureCount}</div>
3322
+ <div>failureReason {query.failureReason}</div>
3301
3323
  </div>
3302
3324
  )
3303
3325
  }
@@ -3306,12 +3328,14 @@ describe('useQuery', () => {
3306
3328
 
3307
3329
  // The query should display the first error result
3308
3330
  await waitFor(() => rendered.getByText('failureCount 1'))
3331
+ await waitFor(() => rendered.getByText('failureReason fetching error 1'))
3309
3332
  await waitFor(() => rendered.getByText('status loading'))
3310
3333
  await waitFor(() => rendered.getByText('error null'))
3311
3334
 
3312
3335
  // Check if the query really paused
3313
3336
  await sleep(10)
3314
3337
  await waitFor(() => rendered.getByText('failureCount 1'))
3338
+ await waitFor(() => rendered.getByText('failureReason fetching error 1'))
3315
3339
 
3316
3340
  act(() => {
3317
3341
  // reset visibilityState to original value
@@ -3321,12 +3345,14 @@ describe('useQuery', () => {
3321
3345
 
3322
3346
  // Wait for the final result
3323
3347
  await waitFor(() => rendered.getByText('failureCount 4'))
3348
+ await waitFor(() => rendered.getByText('failureReason fetching error 4'))
3324
3349
  await waitFor(() => rendered.getByText('status error'))
3325
3350
  await waitFor(() => rendered.getByText('error fetching error 4'))
3326
3351
 
3327
3352
  // Check if the query really stopped
3328
3353
  await sleep(10)
3329
3354
  await waitFor(() => rendered.getByText('failureCount 4'))
3355
+ await waitFor(() => rendered.getByText('failureReason fetching error 4'))
3330
3356
 
3331
3357
  // Check if the error has been logged in the console
3332
3358
  expect(mockLogger.error).toHaveBeenCalledWith('fetching error 4')
@@ -3493,7 +3519,7 @@ describe('useQuery', () => {
3493
3519
  function Page() {
3494
3520
  let counter = 0
3495
3521
 
3496
- const query = useQuery(
3522
+ const query = useQuery<string, Error>(
3497
3523
  key,
3498
3524
  async () => {
3499
3525
  if (counter < 2) {
@@ -3509,6 +3535,7 @@ describe('useQuery', () => {
3509
3535
  return (
3510
3536
  <div>
3511
3537
  <div>failureCount {query.failureCount}</div>
3538
+ <div>failureReason {query.failureReason?.message ?? 'null'}</div>
3512
3539
  </div>
3513
3540
  )
3514
3541
  }
@@ -3516,7 +3543,9 @@ describe('useQuery', () => {
3516
3543
  const rendered = renderWithClient(queryClient, <Page />)
3517
3544
 
3518
3545
  await waitFor(() => rendered.getByText('failureCount 2'))
3546
+ await waitFor(() => rendered.getByText('failureReason error'))
3519
3547
  await waitFor(() => rendered.getByText('failureCount 0'))
3548
+ await waitFor(() => rendered.getByText('failureReason null'))
3520
3549
  })
3521
3550
 
3522
3551
  // See https://github.com/tannerlinsley/react-query/issues/199
@@ -4909,7 +4938,7 @@ describe('useQuery', () => {
4909
4938
  let count = 0
4910
4939
 
4911
4940
  function Page() {
4912
- const state = useQuery({
4941
+ const state = useQuery<string, string>({
4913
4942
  queryKey: key,
4914
4943
  queryFn: async () => {
4915
4944
  count++
@@ -4924,6 +4953,7 @@ describe('useQuery', () => {
4924
4953
  status: {state.status}, fetchStatus: {state.fetchStatus},
4925
4954
  failureCount: {state.failureCount}
4926
4955
  </div>
4956
+ <div>failureReason: {state.failureReason ?? 'null'}</div>
4927
4957
  <div>data: {state.data}</div>
4928
4958
  <button
4929
4959
  onClick={() => queryClient.invalidateQueries({ queryKey: key })}
@@ -4946,6 +4976,7 @@ describe('useQuery', () => {
4946
4976
  'status: success, fetchStatus: paused, failureCount: 0',
4947
4977
  ),
4948
4978
  )
4979
+ await waitFor(() => rendered.getByText('failureReason: null'))
4949
4980
 
4950
4981
  onlineMock.mockReturnValue(true)
4951
4982
  window.dispatchEvent(new Event('online'))
@@ -4955,11 +4986,13 @@ describe('useQuery', () => {
4955
4986
  'status: success, fetchStatus: fetching, failureCount: 0',
4956
4987
  ),
4957
4988
  )
4989
+ await waitFor(() => rendered.getByText('failureReason: null'))
4958
4990
  await waitFor(() =>
4959
4991
  rendered.getByText(
4960
4992
  'status: success, fetchStatus: idle, failureCount: 0',
4961
4993
  ),
4962
4994
  )
4995
+ await waitFor(() => rendered.getByText('failureReason: null'))
4963
4996
 
4964
4997
  await waitFor(() => {
4965
4998
  expect(rendered.getByText('data: data2')).toBeInTheDocument()
@@ -5198,7 +5231,7 @@ describe('useQuery', () => {
5198
5231
  let count = 0
5199
5232
 
5200
5233
  function Page() {
5201
- const state = useQuery({
5234
+ const state = useQuery<unknown, Error>({
5202
5235
  queryKey: key,
5203
5236
  queryFn: async (): Promise<unknown> => {
5204
5237
  count++
@@ -5215,6 +5248,7 @@ describe('useQuery', () => {
5215
5248
  status: {state.status}, fetchStatus: {state.fetchStatus},
5216
5249
  failureCount: {state.failureCount}
5217
5250
  </div>
5251
+ <div>failureReason: {state.failureReason?.message ?? 'null'}</div>
5218
5252
  </div>
5219
5253
  )
5220
5254
  }
@@ -5226,6 +5260,7 @@ describe('useQuery', () => {
5226
5260
  'status: loading, fetchStatus: fetching, failureCount: 1',
5227
5261
  ),
5228
5262
  )
5263
+ await waitFor(() => rendered.getByText('failureReason: failed1'))
5229
5264
 
5230
5265
  const onlineMock = mockNavigatorOnLine(false)
5231
5266
 
@@ -5236,6 +5271,7 @@ describe('useQuery', () => {
5236
5271
  'status: loading, fetchStatus: paused, failureCount: 1',
5237
5272
  ),
5238
5273
  )
5274
+ await waitFor(() => rendered.getByText('failureReason: failed1'))
5239
5275
 
5240
5276
  expect(count).toBe(1)
5241
5277
 
@@ -5245,6 +5281,7 @@ describe('useQuery', () => {
5245
5281
  await waitFor(() =>
5246
5282
  rendered.getByText('status: error, fetchStatus: idle, failureCount: 3'),
5247
5283
  )
5284
+ await waitFor(() => rendered.getByText('failureReason: failed3'))
5248
5285
 
5249
5286
  expect(count).toBe(3)
5250
5287
 
@@ -5539,7 +5576,7 @@ describe('useQuery', () => {
5539
5576
  let count = 0
5540
5577
 
5541
5578
  function Page() {
5542
- const state = useQuery({
5579
+ const state = useQuery<unknown, Error>({
5543
5580
  queryKey: key,
5544
5581
  queryFn: async (): Promise<unknown> => {
5545
5582
  count++
@@ -5557,6 +5594,7 @@ describe('useQuery', () => {
5557
5594
  status: {state.status}, fetchStatus: {state.fetchStatus},
5558
5595
  failureCount: {state.failureCount}
5559
5596
  </div>
5597
+ <div>failureReason: {state.failureReason?.message ?? 'null'}</div>
5560
5598
  </div>
5561
5599
  )
5562
5600
  }
@@ -5568,6 +5606,7 @@ describe('useQuery', () => {
5568
5606
  'status: loading, fetchStatus: paused, failureCount: 1',
5569
5607
  ),
5570
5608
  )
5609
+ await waitFor(() => rendered.getByText('failureReason: failed1'))
5571
5610
 
5572
5611
  expect(count).toBe(1)
5573
5612
 
@@ -5577,6 +5616,7 @@ describe('useQuery', () => {
5577
5616
  await waitFor(() =>
5578
5617
  rendered.getByText('status: error, fetchStatus: idle, failureCount: 3'),
5579
5618
  )
5619
+ await waitFor(() => rendered.getByText('failureReason: failed3'))
5580
5620
 
5581
5621
  expect(count).toBe(3)
5582
5622