@tanstack/solid-query 4.12.0 → 4.13.4

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.
@@ -39,6 +39,7 @@ describe('useInfiniteQuery', () => {
39
39
  error: null,
40
40
  errorUpdatedAt: 0,
41
41
  failureCount: 0,
42
+ failureReason: null,
42
43
  errorUpdateCount: 0,
43
44
  fetchNextPage: expect.any(Function),
44
45
  fetchPreviousPage: expect.any(Function),
@@ -71,6 +72,7 @@ describe('useInfiniteQuery', () => {
71
72
  error: null,
72
73
  errorUpdatedAt: 0,
73
74
  failureCount: 0,
75
+ failureReason: null,
74
76
  errorUpdateCount: 0,
75
77
  fetchNextPage: expect.any(Function),
76
78
  fetchPreviousPage: expect.any(Function),
@@ -103,6 +103,47 @@ describe('useMutation', () => {
103
103
  expect(onSettledMock).toHaveBeenCalledWith(2);
104
104
  expect(onSettledMock).toHaveBeenCalledWith(3);
105
105
  });
106
+ it('should set correct values for `failureReason` and `failureCount` on multiple mutate calls', async () => {
107
+ const [count, setCount] = createSignal(0);
108
+ const mutateFn = jest.fn();
109
+ mutateFn.mockImplementationOnce(() => {
110
+ return Promise.reject('Error test Jonas');
111
+ });
112
+ mutateFn.mockImplementation(async (value) => {
113
+ await sleep(10);
114
+ return Promise.resolve(value);
115
+ });
116
+ function Page() {
117
+ const mutation = createMutation(mutateFn);
118
+ return (<div>
119
+ <h1>Data {mutation.data?.count}</h1>
120
+ <h2>Status {mutation.status}</h2>
121
+ <h2>Failed {mutation.failureCount} times</h2>
122
+ <h2>Failed because {mutation.failureReason ?? 'null'}</h2>
123
+ <button onClick={() => {
124
+ setCount((c) => c + 1);
125
+ return mutation.mutate({ count: count() });
126
+ }}>
127
+ mutate
128
+ </button>
129
+ </div>);
130
+ }
131
+ render(() => (<QueryClientProvider client={queryClient}>
132
+ <Page />
133
+ </QueryClientProvider>));
134
+ await waitFor(() => screen.getByText('Data'));
135
+ fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
136
+ await waitFor(() => screen.getByText('Data'));
137
+ await waitFor(() => screen.getByText('Status error'));
138
+ await waitFor(() => screen.getByText('Failed 1 times'));
139
+ await waitFor(() => screen.getByText('Failed because Error test Jonas'));
140
+ fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
141
+ await waitFor(() => screen.getByText('Status loading'));
142
+ await waitFor(() => screen.getByText('Status success'));
143
+ await waitFor(() => screen.getByText('Data 2'));
144
+ await waitFor(() => screen.getByText('Failed 0 times'));
145
+ await waitFor(() => screen.getByText('Failed because null'));
146
+ });
106
147
  it('should be able to call `onError` and `onSettled` after each failed mutate', async () => {
107
148
  const onErrorMock = jest.fn();
108
149
  const onSettledMock = jest.fn();
@@ -435,21 +476,25 @@ describe('useMutation', () => {
435
476
  isLoading: false,
436
477
  isPaused: false,
437
478
  failureCount: 0,
479
+ failureReason: null,
438
480
  });
439
481
  expect(states[1]).toMatchObject({
440
482
  isLoading: true,
441
483
  isPaused: false,
442
484
  failureCount: 0,
485
+ failureReason: null,
443
486
  });
444
487
  expect(states[2]).toMatchObject({
445
488
  isLoading: true,
446
489
  isPaused: false,
447
490
  failureCount: 1,
491
+ failureReason: 'oops',
448
492
  });
449
493
  expect(states[3]).toMatchObject({
450
494
  isLoading: true,
451
495
  isPaused: true,
452
496
  failureCount: 1,
497
+ failureReason: 'oops',
453
498
  });
454
499
  onlineMock.mockReturnValue(true);
455
500
  window.dispatchEvent(new Event('online'));
@@ -459,11 +504,13 @@ describe('useMutation', () => {
459
504
  isLoading: true,
460
505
  isPaused: false,
461
506
  failureCount: 1,
507
+ failureReason: 'oops',
462
508
  });
463
509
  expect(states[5]).toMatchObject({
464
510
  isLoading: false,
465
511
  isPaused: false,
466
- failureCount: 1,
512
+ failureCount: 0,
513
+ failureReason: null,
467
514
  data: 'data',
468
515
  });
469
516
  onlineMock.mockRestore();
@@ -145,6 +145,7 @@ describe('createQuery', () => {
145
145
  error: null,
146
146
  errorUpdatedAt: 0,
147
147
  failureCount: 0,
148
+ failureReason: null,
148
149
  errorUpdateCount: 0,
149
150
  isError: false,
150
151
  isFetched: false,
@@ -171,6 +172,7 @@ describe('createQuery', () => {
171
172
  error: null,
172
173
  errorUpdatedAt: 0,
173
174
  failureCount: 0,
175
+ failureReason: null,
174
176
  errorUpdateCount: 0,
175
177
  isError: false,
176
178
  isFetched: true,
@@ -206,6 +208,7 @@ describe('createQuery', () => {
206
208
  return (<div>
207
209
  <h1>Status: {state.status}</h1>
208
210
  <div>Failure Count: {state.failureCount}</div>
211
+ <div>Failure Reason: {state.failureReason}</div>
209
212
  </div>);
210
213
  }
211
214
  render(() => (<QueryClientProvider client={queryClient}>
@@ -218,6 +221,7 @@ describe('createQuery', () => {
218
221
  error: null,
219
222
  errorUpdatedAt: 0,
220
223
  failureCount: 0,
224
+ failureReason: null,
221
225
  errorUpdateCount: 0,
222
226
  isError: false,
223
227
  isFetched: false,
@@ -244,6 +248,7 @@ describe('createQuery', () => {
244
248
  error: null,
245
249
  errorUpdatedAt: 0,
246
250
  failureCount: 1,
251
+ failureReason: 'rejected',
247
252
  errorUpdateCount: 0,
248
253
  isError: false,
249
254
  isFetched: false,
@@ -270,6 +275,7 @@ describe('createQuery', () => {
270
275
  error: 'rejected',
271
276
  errorUpdatedAt: expect.any(Number),
272
277
  failureCount: 2,
278
+ failureReason: 'rejected',
273
279
  errorUpdateCount: 1,
274
280
  isError: true,
275
281
  isFetched: true,
@@ -2329,6 +2335,7 @@ describe('createQuery', () => {
2329
2335
  return (<div>
2330
2336
  <div>error: {result.error ?? 'null'}</div>
2331
2337
  <div>failureCount: {result.failureCount}</div>
2338
+ <div>failureReason: {result.failureReason}</div>
2332
2339
  </div>);
2333
2340
  }
2334
2341
  function App() {
@@ -2343,6 +2350,7 @@ describe('createQuery', () => {
2343
2350
  <App />
2344
2351
  </QueryClientProvider>));
2345
2352
  await waitFor(() => screen.getByText('failureCount: 1'));
2353
+ await waitFor(() => screen.getByText('failureReason: some error'));
2346
2354
  fireEvent.click(screen.getByRole('button', { name: /hide/i }));
2347
2355
  await waitFor(() => screen.getByRole('button', { name: /show/i }));
2348
2356
  fireEvent.click(screen.getByRole('button', { name: /show/i }));
@@ -2364,6 +2372,7 @@ describe('createQuery', () => {
2364
2372
  return (<div>
2365
2373
  <div>error: {result.error ?? 'null'}</div>
2366
2374
  <div>failureCount: {result.failureCount}</div>
2375
+ <div>failureReason: {result.failureReason}</div>
2367
2376
  </div>);
2368
2377
  }
2369
2378
  function App() {
@@ -2381,6 +2390,7 @@ describe('createQuery', () => {
2381
2390
  <App />
2382
2391
  </QueryClientProvider>));
2383
2392
  await waitFor(() => screen.getByText('failureCount: 1'));
2393
+ await waitFor(() => screen.getByText('failureReason: some error'));
2384
2394
  fireEvent.click(screen.getByRole('button', { name: /hide/i }));
2385
2395
  fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
2386
2396
  await waitFor(() => screen.getByRole('button', { name: /show/i }));
@@ -2594,6 +2604,7 @@ describe('createQuery', () => {
2594
2604
  return (<div>
2595
2605
  <h1>{state.status}</h1>
2596
2606
  <h2>Failed {state.failureCount} times</h2>
2607
+ <h2>Failed because {state.failureReason}</h2>
2597
2608
  </div>);
2598
2609
  }
2599
2610
  render(() => (<QueryClientProvider client={queryClient}>
@@ -2603,6 +2614,7 @@ describe('createQuery', () => {
2603
2614
  await waitFor(() => screen.getByText('error'));
2604
2615
  // query should fail `retry + 1` times, since first time isn't a "retry"
2605
2616
  await waitFor(() => screen.getByText('Failed 2 times'));
2617
+ await waitFor(() => screen.getByText('Failed because Error test Barrett'));
2606
2618
  expect(queryFn).toHaveBeenCalledTimes(2);
2607
2619
  });
2608
2620
  it('should not retry if retry function `false`', async () => {
@@ -2622,6 +2634,7 @@ describe('createQuery', () => {
2622
2634
  return (<div>
2623
2635
  <h1>{state.status}</h1>
2624
2636
  <h2>Failed {state.failureCount} times</h2>
2637
+ <h2>Failed because {state.failureReason}</h2>
2625
2638
  <h2>{state.error}</h2>
2626
2639
  </div>);
2627
2640
  }
@@ -2631,6 +2644,7 @@ describe('createQuery', () => {
2631
2644
  await waitFor(() => screen.getByText('loading'));
2632
2645
  await waitFor(() => screen.getByText('error'));
2633
2646
  await waitFor(() => screen.getByText('Failed 2 times'));
2647
+ await waitFor(() => screen.getByText('Failed because NoRetry'));
2634
2648
  await waitFor(() => screen.getByText('NoRetry'));
2635
2649
  expect(queryFn).toHaveBeenCalledTimes(2);
2636
2650
  });
@@ -2648,6 +2662,7 @@ describe('createQuery', () => {
2648
2662
  return (<div>
2649
2663
  <h1>{state.status}</h1>
2650
2664
  <h2>Failed {state.failureCount} times</h2>
2665
+ <h2>Failed because DelayError: {state.failureReason?.delay}ms</h2>
2651
2666
  </div>);
2652
2667
  }
2653
2668
  render(() => (<QueryClientProvider client={queryClient}>
@@ -2655,6 +2670,7 @@ describe('createQuery', () => {
2655
2670
  </QueryClientProvider>));
2656
2671
  await sleep(10);
2657
2672
  expect(queryFn).toHaveBeenCalledTimes(1);
2673
+ await waitFor(() => screen.getByText('Failed because DelayError: 50ms'));
2658
2674
  await waitFor(() => screen.getByText('Failed 2 times'));
2659
2675
  expect(queryFn).toHaveBeenCalledTimes(2);
2660
2676
  });
@@ -2676,6 +2692,7 @@ describe('createQuery', () => {
2676
2692
  <div>error {String(query.error)}</div>
2677
2693
  <div>status {query.status}</div>
2678
2694
  <div>failureCount {query.failureCount}</div>
2695
+ <div>failureReason {query.failureReason}</div>
2679
2696
  </div>);
2680
2697
  }
2681
2698
  render(() => (<QueryClientProvider client={queryClient}>
@@ -2683,20 +2700,24 @@ describe('createQuery', () => {
2683
2700
  </QueryClientProvider>));
2684
2701
  // The query should display the first error result
2685
2702
  await waitFor(() => screen.getByText('failureCount 1'));
2703
+ await waitFor(() => screen.getByText('failureReason fetching error 1'));
2686
2704
  await waitFor(() => screen.getByText('status loading'));
2687
2705
  await waitFor(() => screen.getByText('error null'));
2688
2706
  // Check if the query really paused
2689
2707
  await sleep(10);
2690
2708
  await waitFor(() => screen.getByText('failureCount 1'));
2709
+ await waitFor(() => screen.getByText('failureReason fetching error 1'));
2691
2710
  visibilityMock.mockRestore();
2692
2711
  window.dispatchEvent(new FocusEvent('focus'));
2693
2712
  // Wait for the final result
2694
2713
  await waitFor(() => screen.getByText('failureCount 4'));
2714
+ await waitFor(() => screen.getByText('failureReason fetching error 4'));
2695
2715
  await waitFor(() => screen.getByText('status error'));
2696
2716
  await waitFor(() => screen.getByText('error fetching error 4'));
2697
2717
  // Check if the query really stopped
2698
2718
  await sleep(10);
2699
2719
  await waitFor(() => screen.getByText('failureCount 4'));
2720
+ await waitFor(() => screen.getByText('failureReason fetching error 4'));
2700
2721
  // Check if the error has been logged in the console
2701
2722
  expect(mockLogger.error).toHaveBeenCalledWith('fetching error 4');
2702
2723
  });
@@ -2832,7 +2853,7 @@ describe('createQuery', () => {
2832
2853
  expect(queryFn).toHaveBeenCalledTimes(0);
2833
2854
  });
2834
2855
  // See https://github.com/tannerlinsley/react-query/issues/190
2835
- it('should reset failureCount on successful fetch', async () => {
2856
+ it('should reset failureCount and failureReason on successful fetch', async () => {
2836
2857
  const key = queryKey();
2837
2858
  function Page() {
2838
2859
  let counter = 0;
@@ -2847,13 +2868,16 @@ describe('createQuery', () => {
2847
2868
  }, { retryDelay: 10 });
2848
2869
  return (<div>
2849
2870
  <div>failureCount {query.failureCount}</div>
2871
+ <div>failureReason {query.failureReason?.message ?? 'null'}</div>
2850
2872
  </div>);
2851
2873
  }
2852
2874
  render(() => (<QueryClientProvider client={queryClient}>
2853
2875
  <Page />
2854
2876
  </QueryClientProvider>));
2855
2877
  await waitFor(() => screen.getByText('failureCount 2'));
2878
+ await waitFor(() => screen.getByText('failureReason error'));
2856
2879
  await waitFor(() => screen.getByText('failureCount 0'));
2880
+ await waitFor(() => screen.getByText('failureReason null'));
2857
2881
  });
2858
2882
  // See https://github.com/tannerlinsley/react-query/issues/199
2859
2883
  it('should use prefetched data for dependent query', async () => {
@@ -4014,6 +4038,7 @@ describe('createQuery', () => {
4014
4038
  status: {state.status}, fetchStatus: {state.fetchStatus},
4015
4039
  failureCount: {state.failureCount}
4016
4040
  </div>
4041
+ <div>failureReason: {state.failureReason ?? 'null'}</div>
4017
4042
  <div>data: {state.data}</div>
4018
4043
  <button onClick={() => queryClient.invalidateQueries({ queryKey: key() })}>
4019
4044
  invalidate
@@ -4027,10 +4052,13 @@ describe('createQuery', () => {
4027
4052
  const onlineMock = mockNavigatorOnLine(false);
4028
4053
  fireEvent.click(screen.getByRole('button', { name: /invalidate/i }));
4029
4054
  await waitFor(() => screen.getByText('status: success, fetchStatus: paused, failureCount: 0'));
4055
+ await waitFor(() => screen.getByText('failureReason: null'));
4030
4056
  onlineMock.mockReturnValue(true);
4031
4057
  window.dispatchEvent(new Event('online'));
4032
4058
  await waitFor(() => screen.getByText('status: success, fetchStatus: fetching, failureCount: 0'));
4059
+ await waitFor(() => screen.getByText('failureReason: null'));
4033
4060
  await waitFor(() => screen.getByText('status: success, fetchStatus: idle, failureCount: 0'));
4061
+ await waitFor(() => screen.getByText('failureReason: null'));
4034
4062
  await waitFor(() => {
4035
4063
  expect(screen.getByText('data: data2')).toBeInTheDocument();
4036
4064
  });
@@ -4208,19 +4236,23 @@ describe('createQuery', () => {
4208
4236
  status: {state.status}, fetchStatus: {state.fetchStatus},
4209
4237
  failureCount: {state.failureCount}
4210
4238
  </div>
4239
+ <div>failureReason: {state.failureReason?.message ?? 'null'}</div>
4211
4240
  </div>);
4212
4241
  }
4213
4242
  render(() => (<QueryClientProvider client={queryClient}>
4214
4243
  <Page />
4215
4244
  </QueryClientProvider>));
4216
4245
  await waitFor(() => screen.getByText('status: loading, fetchStatus: fetching, failureCount: 1'));
4246
+ await waitFor(() => screen.getByText('failureReason: failed1'));
4217
4247
  const onlineMock = mockNavigatorOnLine(false);
4218
4248
  await sleep(20);
4219
4249
  await waitFor(() => screen.getByText('status: loading, fetchStatus: paused, failureCount: 1'));
4250
+ await waitFor(() => screen.getByText('failureReason: failed1'));
4220
4251
  expect(count).toBe(1);
4221
4252
  onlineMock.mockReturnValue(true);
4222
4253
  window.dispatchEvent(new Event('online'));
4223
4254
  await waitFor(() => screen.getByText('status: error, fetchStatus: idle, failureCount: 3'));
4255
+ await waitFor(() => screen.getByText('failureReason: failed3'));
4224
4256
  expect(count).toBe(3);
4225
4257
  onlineMock.mockRestore();
4226
4258
  });
@@ -4442,16 +4474,19 @@ describe('createQuery', () => {
4442
4474
  status: {state.status}, fetchStatus: {state.fetchStatus},
4443
4475
  failureCount: {state.failureCount}
4444
4476
  </div>
4477
+ <div>failureReason: {state.failureReason?.message ?? 'null'}</div>
4445
4478
  </div>);
4446
4479
  }
4447
4480
  render(() => (<QueryClientProvider client={queryClient}>
4448
4481
  <Page />
4449
4482
  </QueryClientProvider>));
4450
4483
  await waitFor(() => screen.getByText('status: loading, fetchStatus: paused, failureCount: 1'));
4484
+ await waitFor(() => screen.getByText('failureReason: failed1'));
4451
4485
  expect(count).toBe(1);
4452
4486
  onlineMock.mockReturnValue(true);
4453
4487
  window.dispatchEvent(new Event('online'));
4454
4488
  await waitFor(() => screen.getByText('status: error, fetchStatus: idle, failureCount: 3'));
4489
+ await waitFor(() => screen.getByText('failureReason: failed3'));
4455
4490
  expect(count).toBe(3);
4456
4491
  onlineMock.mockRestore();
4457
4492
  });
@@ -1112,9 +1112,11 @@
1112
1112
  this.isFetchingOptimistic = false;
1113
1113
  },
1114
1114
  onError,
1115
- onFail: () => {
1115
+ onFail: (failureCount, error) => {
1116
1116
  this.dispatch({
1117
- type: 'failed'
1117
+ type: 'failed',
1118
+ failureCount,
1119
+ error
1118
1120
  });
1119
1121
  },
1120
1122
  onPause: () => {
@@ -1142,7 +1144,8 @@
1142
1144
  switch (action.type) {
1143
1145
  case 'failed':
1144
1146
  return { ...state,
1145
- fetchFailureCount: state.fetchFailureCount + 1
1147
+ fetchFailureCount: action.failureCount,
1148
+ fetchFailureReason: action.error
1146
1149
  };
1147
1150
 
1148
1151
  case 'pause':
@@ -1158,6 +1161,7 @@
1158
1161
  case 'fetch':
1159
1162
  return { ...state,
1160
1163
  fetchFailureCount: 0,
1164
+ fetchFailureReason: null,
1161
1165
  fetchMeta: (_action$meta = action.meta) != null ? _action$meta : null,
1162
1166
  fetchStatus: canFetch(this.options.networkMode) ? 'fetching' : 'paused',
1163
1167
  ...(!state.dataUpdatedAt && {
@@ -1176,7 +1180,8 @@
1176
1180
  status: 'success',
1177
1181
  ...(!action.manual && {
1178
1182
  fetchStatus: 'idle',
1179
- fetchFailureCount: 0
1183
+ fetchFailureCount: 0,
1184
+ fetchFailureReason: null
1180
1185
  })
1181
1186
  };
1182
1187
 
@@ -1193,6 +1198,7 @@
1193
1198
  errorUpdateCount: state.errorUpdateCount + 1,
1194
1199
  errorUpdatedAt: Date.now(),
1195
1200
  fetchFailureCount: state.fetchFailureCount + 1,
1201
+ fetchFailureReason: error,
1196
1202
  fetchStatus: 'idle',
1197
1203
  status: 'error'
1198
1204
  };
@@ -1237,6 +1243,7 @@
1237
1243
  errorUpdateCount: 0,
1238
1244
  errorUpdatedAt: 0,
1239
1245
  fetchFailureCount: 0,
1246
+ fetchFailureReason: null,
1240
1247
  fetchMeta: null,
1241
1248
  isInvalidated: false,
1242
1249
  status: hasData ? 'success' : 'loading',
@@ -1441,9 +1448,11 @@
1441
1448
 
1442
1449
  return this.options.mutationFn(this.state.variables);
1443
1450
  },
1444
- onFail: () => {
1451
+ onFail: (failureCount, error) => {
1445
1452
  this.dispatch({
1446
- type: 'failed'
1453
+ type: 'failed',
1454
+ failureCount,
1455
+ error
1447
1456
  });
1448
1457
  },
1449
1458
  onPause: () => {
@@ -1526,7 +1535,8 @@
1526
1535
  switch (action.type) {
1527
1536
  case 'failed':
1528
1537
  return { ...state,
1529
- failureCount: state.failureCount + 1
1538
+ failureCount: action.failureCount,
1539
+ failureReason: action.error
1530
1540
  };
1531
1541
 
1532
1542
  case 'pause':
@@ -1543,6 +1553,8 @@
1543
1553
  return { ...state,
1544
1554
  context: action.context,
1545
1555
  data: undefined,
1556
+ failureCount: 0,
1557
+ failureReason: null,
1546
1558
  error: null,
1547
1559
  isPaused: !canFetch(this.options.networkMode),
1548
1560
  status: 'loading',
@@ -1552,6 +1564,8 @@
1552
1564
  case 'success':
1553
1565
  return { ...state,
1554
1566
  data: action.data,
1567
+ failureCount: 0,
1568
+ failureReason: null,
1555
1569
  error: null,
1556
1570
  status: 'success',
1557
1571
  isPaused: false
@@ -1562,6 +1576,7 @@
1562
1576
  data: undefined,
1563
1577
  error: action.error,
1564
1578
  failureCount: state.failureCount + 1,
1579
+ failureReason: action.error,
1565
1580
  isPaused: false,
1566
1581
  status: 'error'
1567
1582
  };
@@ -1593,6 +1608,7 @@
1593
1608
  data: undefined,
1594
1609
  error: null,
1595
1610
  failureCount: 0,
1611
+ failureReason: null,
1596
1612
  isPaused: false,
1597
1613
  status: 'idle',
1598
1614
  variables: undefined
@@ -2453,7 +2469,6 @@
2453
2469
  if (options.select && typeof placeholderData !== 'undefined') {
2454
2470
  try {
2455
2471
  placeholderData = options.select(placeholderData);
2456
- placeholderData = replaceData(prevResult == null ? void 0 : prevResult.data, placeholderData, options);
2457
2472
  this.selectError = null;
2458
2473
  } catch (selectError) {
2459
2474
  {
@@ -2467,7 +2482,7 @@
2467
2482
 
2468
2483
  if (typeof placeholderData !== 'undefined') {
2469
2484
  status = 'success';
2470
- data = placeholderData;
2485
+ data = replaceData(prevResult == null ? void 0 : prevResult.data, placeholderData, options);
2471
2486
  isPlaceholderData = true;
2472
2487
  }
2473
2488
  }
@@ -2494,6 +2509,7 @@
2494
2509
  error,
2495
2510
  errorUpdatedAt,
2496
2511
  failureCount: state.fetchFailureCount,
2512
+ failureReason: state.fetchFailureReason,
2497
2513
  errorUpdateCount: state.errorUpdateCount,
2498
2514
  isFetched: state.dataUpdateCount > 0 || state.errorUpdateCount > 0,
2499
2515
  isFetchedAfterMount: state.dataUpdateCount > queryInitialState.dataUpdateCount || state.errorUpdateCount > queryInitialState.errorUpdateCount,