@tanstack/query-core 4.25.0 → 4.26.1

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.
Files changed (40) hide show
  1. package/build/lib/mutation.d.ts +2 -0
  2. package/build/lib/mutation.esm.js +18 -9
  3. package/build/lib/mutation.esm.js.map +1 -1
  4. package/build/lib/mutation.js +18 -9
  5. package/build/lib/mutation.js.map +1 -1
  6. package/build/lib/mutation.mjs +18 -9
  7. package/build/lib/mutation.mjs.map +1 -1
  8. package/build/lib/mutationCache.d.ts +2 -1
  9. package/build/lib/mutationCache.esm.js.map +1 -1
  10. package/build/lib/mutationCache.js.map +1 -1
  11. package/build/lib/mutationCache.mjs.map +1 -1
  12. package/build/lib/mutationObserver.esm.js +6 -2
  13. package/build/lib/mutationObserver.esm.js.map +1 -1
  14. package/build/lib/mutationObserver.js +6 -2
  15. package/build/lib/mutationObserver.js.map +1 -1
  16. package/build/lib/mutationObserver.mjs +6 -2
  17. package/build/lib/mutationObserver.mjs.map +1 -1
  18. package/build/lib/query.esm.js +5 -3
  19. package/build/lib/query.esm.js.map +1 -1
  20. package/build/lib/query.js +5 -3
  21. package/build/lib/query.js.map +1 -1
  22. package/build/lib/query.mjs +5 -3
  23. package/build/lib/query.mjs.map +1 -1
  24. package/build/lib/queryCache.d.ts +1 -0
  25. package/build/lib/queryCache.esm.js.map +1 -1
  26. package/build/lib/queryCache.js.map +1 -1
  27. package/build/lib/queryCache.mjs.map +1 -1
  28. package/build/umd/index.development.js +29 -14
  29. package/build/umd/index.development.js.map +1 -1
  30. package/build/umd/index.production.js +1 -1
  31. package/build/umd/index.production.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/mutation.ts +30 -6
  34. package/src/mutationCache.ts +8 -1
  35. package/src/mutationObserver.ts +1 -0
  36. package/src/query.ts +10 -0
  37. package/src/queryCache.ts +5 -0
  38. package/src/tests/mutationCache.test.tsx +54 -10
  39. package/src/tests/mutations.test.tsx +31 -0
  40. package/src/tests/queryCache.test.tsx +18 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-core",
3
- "version": "4.25.0",
3
+ "version": "4.26.1",
4
4
  "description": "The framework agnostic core that powers TanStack Query",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/mutation.ts CHANGED
@@ -89,10 +89,11 @@ export class Mutation<
89
89
  TContext = unknown,
90
90
  > extends Removable {
91
91
  state: MutationState<TData, TError, TVariables, TContext>
92
- options: MutationOptions<TData, TError, TVariables, TContext>
92
+ options!: MutationOptions<TData, TError, TVariables, TContext>
93
93
  mutationId: number
94
94
 
95
95
  private observers: MutationObserver<TData, TError, TVariables, TContext>[]
96
+ private defaultOptions?: MutationOptions<TData, TError, TVariables, TContext>
96
97
  private mutationCache: MutationCache
97
98
  private logger: Logger
98
99
  private retryer?: Retryer<TData>
@@ -100,20 +101,25 @@ export class Mutation<
100
101
  constructor(config: MutationConfig<TData, TError, TVariables, TContext>) {
101
102
  super()
102
103
 
103
- this.options = {
104
- ...config.defaultOptions,
105
- ...config.options,
106
- }
104
+ this.defaultOptions = config.defaultOptions
107
105
  this.mutationId = config.mutationId
108
106
  this.mutationCache = config.mutationCache
109
107
  this.logger = config.logger || defaultLogger
110
108
  this.observers = []
111
109
  this.state = config.state || getDefaultState()
112
110
 
113
- this.updateCacheTime(this.options.cacheTime)
111
+ this.setOptions(config.options)
114
112
  this.scheduleGc()
115
113
  }
116
114
 
115
+ setOptions(
116
+ options?: MutationOptions<TData, TError, TVariables, TContext>,
117
+ ): void {
118
+ this.options = { ...this.defaultOptions, ...options }
119
+
120
+ this.updateCacheTime(this.options.cacheTime)
121
+ }
122
+
117
123
  get meta(): MutationMeta | undefined {
118
124
  return this.options.meta
119
125
  }
@@ -223,6 +229,15 @@ export class Mutation<
223
229
  this.state.context!,
224
230
  )
225
231
 
232
+ // Notify cache callback
233
+ await this.mutationCache.config.onSettled?.(
234
+ data,
235
+ null,
236
+ this.state.variables,
237
+ this.state.context,
238
+ this as Mutation<unknown, unknown, unknown, unknown>,
239
+ )
240
+
226
241
  await this.options.onSettled?.(
227
242
  data,
228
243
  null,
@@ -252,6 +267,15 @@ export class Mutation<
252
267
  this.state.context,
253
268
  )
254
269
 
270
+ // Notify cache callback
271
+ await this.mutationCache.config.onSettled?.(
272
+ undefined,
273
+ error,
274
+ this.state.variables,
275
+ this.state.context,
276
+ this as Mutation<unknown, unknown, unknown, unknown>,
277
+ )
278
+
255
279
  await this.options.onSettled?.(
256
280
  undefined,
257
281
  error as TError,
@@ -25,7 +25,14 @@ interface MutationCacheConfig {
25
25
  ) => Promise<unknown> | unknown
26
26
  onMutate?: (
27
27
  variables: unknown,
28
- mutation: Mutation<unknown, unknown, unknown, unknown>,
28
+ mutation: Mutation<unknown, unknown, unknown>,
29
+ ) => Promise<unknown> | unknown
30
+ onSettled?: (
31
+ data: unknown | undefined,
32
+ error: unknown | null,
33
+ variables: unknown,
34
+ context: unknown,
35
+ mutation: Mutation<unknown, unknown, unknown>,
29
36
  ) => Promise<unknown> | unknown
30
37
  }
31
38
 
@@ -74,6 +74,7 @@ export class MutationObserver<
74
74
  observer: this,
75
75
  })
76
76
  }
77
+ this.currentMutation?.setOptions(this.options)
77
78
  }
78
79
 
79
80
  protected onUnsubscribe(): void {
package/src/query.ts CHANGED
@@ -434,6 +434,11 @@ export class Query<
434
434
  if (!isCancelledError(error)) {
435
435
  // Notify cache callback
436
436
  this.cache.config.onError?.(error, this as Query<any, any, any, any>)
437
+ this.cache.config.onSettled?.(
438
+ this.state.data,
439
+ error,
440
+ this as Query<any, any, any, any>,
441
+ )
437
442
 
438
443
  if (process.env.NODE_ENV !== 'production') {
439
444
  this.logger.error(error)
@@ -466,6 +471,11 @@ export class Query<
466
471
 
467
472
  // Notify cache callback
468
473
  this.cache.config.onSuccess?.(data, this as Query<any, any, any, any>)
474
+ this.cache.config.onSettled?.(
475
+ data,
476
+ this.state.error,
477
+ this as Query<any, any, any, any>,
478
+ )
469
479
 
470
480
  if (!this.isFetchingOptimistic) {
471
481
  // Schedule query gc after fetching
package/src/queryCache.ts CHANGED
@@ -13,6 +13,11 @@ import type { QueryObserver } from './queryObserver'
13
13
  interface QueryCacheConfig {
14
14
  onError?: (error: unknown, query: Query<unknown, unknown, unknown>) => void
15
15
  onSuccess?: (data: unknown, query: Query<unknown, unknown, unknown>) => void
16
+ onSettled?: (
17
+ data: unknown | undefined,
18
+ error: unknown | null,
19
+ query: Query<unknown, unknown, unknown>,
20
+ ) => void
16
21
  }
17
22
 
18
23
  interface QueryHashMap {
@@ -3,11 +3,13 @@ import { queryKey, sleep, executeMutation, createQueryClient } from './utils'
3
3
  import { MutationCache, MutationObserver } from '..'
4
4
 
5
5
  describe('mutationCache', () => {
6
- describe('MutationCacheConfig.onError', () => {
7
- test('should be called when a mutation errors', async () => {
6
+ describe('MutationCacheConfig error callbacks', () => {
7
+ test('should call onError and onSettled when a mutation errors', async () => {
8
8
  const key = queryKey()
9
9
  const onError = jest.fn()
10
- const testCache = new MutationCache({ onError })
10
+ const onSuccess = jest.fn()
11
+ const onSettled = jest.fn()
12
+ const testCache = new MutationCache({ onError, onSuccess, onSettled })
11
13
  const testClient = createQueryClient({ mutationCache: testCache })
12
14
 
13
15
  try {
@@ -20,7 +22,17 @@ describe('mutationCache', () => {
20
22
  } catch {}
21
23
 
22
24
  const mutation = testCache.getAll()[0]
25
+ expect(onError).toHaveBeenCalledTimes(1)
23
26
  expect(onError).toHaveBeenCalledWith('error', 'vars', 'context', mutation)
27
+ expect(onSuccess).not.toHaveBeenCalled()
28
+ expect(onSettled).toHaveBeenCalledTimes(1)
29
+ expect(onSettled).toHaveBeenCalledWith(
30
+ undefined,
31
+ 'error',
32
+ 'vars',
33
+ 'context',
34
+ mutation,
35
+ )
24
36
  })
25
37
 
26
38
  test('should be awaited', async () => {
@@ -31,7 +43,12 @@ describe('mutationCache', () => {
31
43
  await sleep(1)
32
44
  states.push(2)
33
45
  }
34
- const testCache = new MutationCache({ onError })
46
+ const onSettled = async () => {
47
+ states.push(5)
48
+ await sleep(1)
49
+ states.push(6)
50
+ }
51
+ const testCache = new MutationCache({ onError, onSettled })
35
52
  const testClient = createQueryClient({ mutationCache: testCache })
36
53
 
37
54
  try {
@@ -44,17 +61,24 @@ describe('mutationCache', () => {
44
61
  await sleep(1)
45
62
  states.push(4)
46
63
  },
64
+ onSettled: async () => {
65
+ states.push(7)
66
+ await sleep(1)
67
+ states.push(8)
68
+ },
47
69
  })
48
70
  } catch {}
49
71
 
50
- expect(states).toEqual([1, 2, 3, 4])
72
+ expect(states).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
51
73
  })
52
74
  })
53
- describe('MutationCacheConfig.onSuccess', () => {
54
- test('should be called when a mutation is successful', async () => {
75
+ describe('MutationCacheConfig success callbacks', () => {
76
+ test('should call onSuccess and onSettled when a mutation is successful', async () => {
55
77
  const key = queryKey()
78
+ const onError = jest.fn()
56
79
  const onSuccess = jest.fn()
57
- const testCache = new MutationCache({ onSuccess })
80
+ const onSettled = jest.fn()
81
+ const testCache = new MutationCache({ onError, onSuccess, onSettled })
58
82
  const testClient = createQueryClient({ mutationCache: testCache })
59
83
 
60
84
  try {
@@ -67,12 +91,22 @@ describe('mutationCache', () => {
67
91
  } catch {}
68
92
 
69
93
  const mutation = testCache.getAll()[0]
94
+ expect(onSuccess).toHaveBeenCalledTimes(1)
70
95
  expect(onSuccess).toHaveBeenCalledWith(
71
96
  { data: 5 },
72
97
  'vars',
73
98
  'context',
74
99
  mutation,
75
100
  )
101
+ expect(onError).not.toHaveBeenCalled()
102
+ expect(onSettled).toHaveBeenCalledTimes(1)
103
+ expect(onSettled).toHaveBeenCalledWith(
104
+ { data: 5 },
105
+ null,
106
+ 'vars',
107
+ 'context',
108
+ mutation,
109
+ )
76
110
  })
77
111
  test('should be awaited', async () => {
78
112
  const key = queryKey()
@@ -82,7 +116,12 @@ describe('mutationCache', () => {
82
116
  await sleep(1)
83
117
  states.push(2)
84
118
  }
85
- const testCache = new MutationCache({ onSuccess })
119
+ const onSettled = async () => {
120
+ states.push(5)
121
+ await sleep(1)
122
+ states.push(6)
123
+ }
124
+ const testCache = new MutationCache({ onSuccess, onSettled })
86
125
  const testClient = createQueryClient({ mutationCache: testCache })
87
126
 
88
127
  await executeMutation(testClient, {
@@ -94,9 +133,14 @@ describe('mutationCache', () => {
94
133
  await sleep(1)
95
134
  states.push(4)
96
135
  },
136
+ onSettled: async () => {
137
+ states.push(7)
138
+ await sleep(1)
139
+ states.push(8)
140
+ },
97
141
  })
98
142
 
99
- expect(states).toEqual([1, 2, 3, 4])
143
+ expect(states).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
100
144
  })
101
145
  })
102
146
  describe('MutationCacheConfig.onMutate', () => {
@@ -2,6 +2,7 @@ import type { QueryClient } from '..'
2
2
  import { createQueryClient, executeMutation, queryKey, sleep } from './utils'
3
3
  import type { MutationState } from '../mutation'
4
4
  import { MutationObserver } from '../mutationObserver'
5
+ import { waitFor } from '@testing-library/react'
5
6
 
6
7
  describe('mutations', () => {
7
8
  let queryClient: QueryClient
@@ -358,4 +359,34 @@ describe('mutations', () => {
358
359
  expect(onSuccess).not.toHaveBeenCalled()
359
360
  expect(onSettled).not.toHaveBeenCalled()
360
361
  })
362
+
363
+ test('mutation callbacks should see updated options', async () => {
364
+ const onSuccess = jest.fn()
365
+
366
+ const mutation = new MutationObserver(queryClient, {
367
+ mutationFn: async () => {
368
+ sleep(100)
369
+ return 'update'
370
+ },
371
+ onSuccess: () => {
372
+ onSuccess(1)
373
+ },
374
+ })
375
+
376
+ void mutation.mutate()
377
+
378
+ mutation.setOptions({
379
+ mutationFn: async () => {
380
+ sleep(100)
381
+ return 'update'
382
+ },
383
+ onSuccess: () => {
384
+ onSuccess(2)
385
+ },
386
+ })
387
+
388
+ await waitFor(() => expect(onSuccess).toHaveBeenCalledTimes(1))
389
+
390
+ expect(onSuccess).toHaveBeenCalledWith(2)
391
+ })
361
392
  })
@@ -205,29 +205,41 @@ describe('queryCache', () => {
205
205
  })
206
206
  })
207
207
 
208
- describe('QueryCacheConfig.onError', () => {
209
- test('should be called when a query errors', async () => {
208
+ describe('QueryCacheConfig error callbacks', () => {
209
+ test('should call onError and onSettled when a query errors', async () => {
210
210
  const key = queryKey()
211
+ const onSuccess = jest.fn()
212
+ const onSettled = jest.fn()
211
213
  const onError = jest.fn()
212
- const testCache = new QueryCache({ onError })
214
+ const testCache = new QueryCache({ onSuccess, onError, onSettled })
213
215
  const testClient = createQueryClient({ queryCache: testCache })
214
216
  await testClient.prefetchQuery(key, () =>
215
217
  Promise.reject<unknown>('error'),
216
218
  )
217
219
  const query = testCache.find(key)
220
+ expect(onError).toHaveBeenCalledTimes(1)
218
221
  expect(onError).toHaveBeenCalledWith('error', query)
222
+ expect(onSuccess).not.toHaveBeenCalled()
223
+ expect(onSettled).toHaveBeenCalledTimes(1)
224
+ expect(onSettled).toHaveBeenCalledWith(undefined, 'error', query)
219
225
  })
220
226
  })
221
227
 
222
- describe('QueryCacheConfig.onSuccess', () => {
223
- test('should be called when a query is successful', async () => {
228
+ describe('QueryCacheConfig success callbacks', () => {
229
+ test('should call onSuccess and onSettled when a query is successful', async () => {
224
230
  const key = queryKey()
225
231
  const onSuccess = jest.fn()
226
- const testCache = new QueryCache({ onSuccess })
232
+ const onSettled = jest.fn()
233
+ const onError = jest.fn()
234
+ const testCache = new QueryCache({ onSuccess, onError, onSettled })
227
235
  const testClient = createQueryClient({ queryCache: testCache })
228
236
  await testClient.prefetchQuery(key, () => Promise.resolve({ data: 5 }))
229
237
  const query = testCache.find(key)
238
+ expect(onSuccess).toHaveBeenCalledTimes(1)
230
239
  expect(onSuccess).toHaveBeenCalledWith({ data: 5 }, query)
240
+ expect(onError).not.toHaveBeenCalled()
241
+ expect(onSettled).toHaveBeenCalledTimes(1)
242
+ expect(onSettled).toHaveBeenCalledWith({ data: 5 }, null, query)
231
243
  })
232
244
  })
233
245