@tanstack/query-core 5.21.2 → 5.21.7

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 (120) hide show
  1. package/build/legacy/hydration.d.cts +1 -1
  2. package/build/legacy/hydration.d.ts +1 -1
  3. package/build/legacy/index.d.cts +1 -1
  4. package/build/legacy/index.d.ts +1 -1
  5. package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
  6. package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
  7. package/build/legacy/infiniteQueryObserver.cjs.map +1 -1
  8. package/build/legacy/infiniteQueryObserver.d.cts +2 -2
  9. package/build/legacy/infiniteQueryObserver.d.ts +2 -2
  10. package/build/legacy/infiniteQueryObserver.js.map +1 -1
  11. package/build/legacy/mutation.d.cts +1 -1
  12. package/build/legacy/mutation.d.ts +1 -1
  13. package/build/legacy/mutationCache.d.cts +1 -1
  14. package/build/legacy/mutationCache.d.ts +1 -1
  15. package/build/legacy/mutationObserver.cjs +1 -1
  16. package/build/legacy/mutationObserver.cjs.map +1 -1
  17. package/build/legacy/mutationObserver.d.cts +1 -1
  18. package/build/legacy/mutationObserver.d.ts +1 -1
  19. package/build/legacy/mutationObserver.js +1 -1
  20. package/build/legacy/mutationObserver.js.map +1 -1
  21. package/build/legacy/queriesObserver.d.cts +1 -1
  22. package/build/legacy/queriesObserver.d.ts +1 -1
  23. package/build/legacy/query.d.cts +1 -1
  24. package/build/legacy/query.d.ts +1 -1
  25. package/build/legacy/queryCache.cjs.map +1 -1
  26. package/build/legacy/queryCache.d.cts +1 -1
  27. package/build/legacy/queryCache.d.ts +1 -1
  28. package/build/legacy/queryCache.js.map +1 -1
  29. package/build/legacy/{queryClient-Xp52MRx8.d.ts → queryClient-Ho-z40Sw.d.ts} +11 -10
  30. package/build/legacy/{queryClient-Wk_JAAGO.d.cts → queryClient-xw4cqSqb.d.cts} +11 -10
  31. package/build/legacy/queryClient.cjs +10 -6
  32. package/build/legacy/queryClient.cjs.map +1 -1
  33. package/build/legacy/queryClient.d.cts +1 -1
  34. package/build/legacy/queryClient.d.ts +1 -1
  35. package/build/legacy/queryClient.js +10 -6
  36. package/build/legacy/queryClient.js.map +1 -1
  37. package/build/legacy/queryObserver.cjs +1 -4
  38. package/build/legacy/queryObserver.cjs.map +1 -1
  39. package/build/legacy/queryObserver.d.cts +1 -1
  40. package/build/legacy/queryObserver.d.ts +1 -1
  41. package/build/legacy/queryObserver.js +1 -4
  42. package/build/legacy/queryObserver.js.map +1 -1
  43. package/build/legacy/retryer.d.cts +1 -1
  44. package/build/legacy/retryer.d.ts +1 -1
  45. package/build/legacy/types.cjs.map +1 -1
  46. package/build/legacy/types.d.cts +1 -1
  47. package/build/legacy/types.d.ts +1 -1
  48. package/build/legacy/utils.cjs +1 -1
  49. package/build/legacy/utils.cjs.map +1 -1
  50. package/build/legacy/utils.d.cts +1 -1
  51. package/build/legacy/utils.d.ts +1 -1
  52. package/build/legacy/utils.js +1 -1
  53. package/build/legacy/utils.js.map +1 -1
  54. package/build/modern/hydration.d.cts +1 -1
  55. package/build/modern/hydration.d.ts +1 -1
  56. package/build/modern/index.d.cts +1 -1
  57. package/build/modern/index.d.ts +1 -1
  58. package/build/modern/infiniteQueryBehavior.d.cts +1 -1
  59. package/build/modern/infiniteQueryBehavior.d.ts +1 -1
  60. package/build/modern/infiniteQueryObserver.cjs.map +1 -1
  61. package/build/modern/infiniteQueryObserver.d.cts +2 -2
  62. package/build/modern/infiniteQueryObserver.d.ts +2 -2
  63. package/build/modern/infiniteQueryObserver.js.map +1 -1
  64. package/build/modern/mutation.d.cts +1 -1
  65. package/build/modern/mutation.d.ts +1 -1
  66. package/build/modern/mutationCache.d.cts +1 -1
  67. package/build/modern/mutationCache.d.ts +1 -1
  68. package/build/modern/mutationObserver.cjs +1 -1
  69. package/build/modern/mutationObserver.cjs.map +1 -1
  70. package/build/modern/mutationObserver.d.cts +1 -1
  71. package/build/modern/mutationObserver.d.ts +1 -1
  72. package/build/modern/mutationObserver.js +1 -1
  73. package/build/modern/mutationObserver.js.map +1 -1
  74. package/build/modern/queriesObserver.d.cts +1 -1
  75. package/build/modern/queriesObserver.d.ts +1 -1
  76. package/build/modern/query.d.cts +1 -1
  77. package/build/modern/query.d.ts +1 -1
  78. package/build/modern/queryCache.cjs.map +1 -1
  79. package/build/modern/queryCache.d.cts +1 -1
  80. package/build/modern/queryCache.d.ts +1 -1
  81. package/build/modern/queryCache.js.map +1 -1
  82. package/build/modern/{queryClient-Xp52MRx8.d.ts → queryClient-Ho-z40Sw.d.ts} +11 -10
  83. package/build/modern/{queryClient-Wk_JAAGO.d.cts → queryClient-xw4cqSqb.d.cts} +11 -10
  84. package/build/modern/queryClient.cjs +10 -6
  85. package/build/modern/queryClient.cjs.map +1 -1
  86. package/build/modern/queryClient.d.cts +1 -1
  87. package/build/modern/queryClient.d.ts +1 -1
  88. package/build/modern/queryClient.js +10 -6
  89. package/build/modern/queryClient.js.map +1 -1
  90. package/build/modern/queryObserver.cjs +1 -4
  91. package/build/modern/queryObserver.cjs.map +1 -1
  92. package/build/modern/queryObserver.d.cts +1 -1
  93. package/build/modern/queryObserver.d.ts +1 -1
  94. package/build/modern/queryObserver.js +1 -4
  95. package/build/modern/queryObserver.js.map +1 -1
  96. package/build/modern/retryer.d.cts +1 -1
  97. package/build/modern/retryer.d.ts +1 -1
  98. package/build/modern/types.cjs.map +1 -1
  99. package/build/modern/types.d.cts +1 -1
  100. package/build/modern/types.d.ts +1 -1
  101. package/build/modern/utils.cjs +1 -1
  102. package/build/modern/utils.cjs.map +1 -1
  103. package/build/modern/utils.d.cts +1 -1
  104. package/build/modern/utils.d.ts +1 -1
  105. package/build/modern/utils.js +1 -1
  106. package/build/modern/utils.js.map +1 -1
  107. package/package.json +1 -1
  108. package/src/infiniteQueryObserver.ts +1 -1
  109. package/src/mutationObserver.ts +1 -1
  110. package/src/queryCache.ts +5 -2
  111. package/src/queryClient.ts +23 -16
  112. package/src/queryObserver.ts +2 -7
  113. package/src/tests/queriesObserver.test.tsx +0 -25
  114. package/src/tests/queryCache.test.tsx +2 -1
  115. package/src/tests/queryClient.test.tsx +42 -0
  116. package/src/tests/queryObserver.test.tsx +3 -3
  117. package/src/tests/queryObserver.types.test.tsx +2 -0
  118. package/src/tests/utils.test.tsx +18 -0
  119. package/src/types.ts +9 -7
  120. package/src/utils.ts +7 -4
@@ -41,7 +41,7 @@ import type { MutationFilters, QueryFilters, Updater } from './utils'
41
41
 
42
42
  interface QueryDefaults {
43
43
  queryKey: QueryKey
44
- defaultOptions: QueryOptions<any, any, any>
44
+ defaultOptions: Omit<QueryOptions<any, any, any>, 'queryKey'>
45
45
  }
46
46
 
47
47
  interface MutationDefaults {
@@ -119,7 +119,8 @@ export class QueryClient {
119
119
  : TQueryFnData,
120
120
  >(queryKey: TTaggedQueryKey): TInferredQueryFnData | undefined
121
121
  getQueryData(queryKey: QueryKey) {
122
- return this.#queryCache.find({ queryKey })?.state.data
122
+ const options = this.defaultQueryOptions({ queryKey })
123
+ return this.#queryCache.get(options.queryHash)?.state.data
123
124
  }
124
125
 
125
126
  ensureQueryData<
@@ -165,14 +166,6 @@ export class QueryClient {
165
166
  >,
166
167
  options?: SetDataOptions,
167
168
  ): TInferredQueryFnData | undefined {
168
- const query = this.#queryCache.find<TInferredQueryFnData>({ queryKey })
169
- const prevData = query?.state.data
170
- const data = functionalUpdate(updater, prevData)
171
-
172
- if (typeof data === 'undefined') {
173
- return undefined
174
- }
175
-
176
169
  const defaultedOptions = this.defaultQueryOptions<
177
170
  any,
178
171
  any,
@@ -181,6 +174,16 @@ export class QueryClient {
181
174
  QueryKey
182
175
  >({ queryKey })
183
176
 
177
+ const query = this.#queryCache.get<TInferredQueryFnData>(
178
+ defaultedOptions.queryHash,
179
+ )
180
+ const prevData = query?.state.data
181
+ const data = functionalUpdate(updater, prevData)
182
+
183
+ if (typeof data === 'undefined') {
184
+ return undefined
185
+ }
186
+
184
187
  return this.#queryCache
185
188
  .build(this, defaultedOptions)
186
189
  .setData(data, { ...options, manual: true })
@@ -204,7 +207,8 @@ export class QueryClient {
204
207
  getQueryState<TQueryFnData = unknown, TError = DefaultError>(
205
208
  queryKey: QueryKey,
206
209
  ): QueryState<TQueryFnData, TError> | undefined {
207
- return this.#queryCache.find<TQueryFnData, TError>({ queryKey })?.state
210
+ const options = this.defaultQueryOptions({ queryKey })
211
+ return this.#queryCache.get<TQueryFnData, TError>(options.queryHash)?.state
208
212
  }
209
213
 
210
214
  removeQueries(filters?: QueryFilters): void {
@@ -409,10 +413,13 @@ export class QueryClient {
409
413
 
410
414
  getQueryDefaults(
411
415
  queryKey: QueryKey,
412
- ): QueryObserverOptions<any, any, any, any, any> {
416
+ ): Omit<QueryObserverOptions<any, any, any, any, any>, 'queryKey'> {
413
417
  const defaults = [...this.#queryDefaults.values()]
414
418
 
415
- let result: QueryObserverOptions<any, any, any, any, any> = {}
419
+ let result: Omit<
420
+ QueryObserverOptions<any, any, any, any, any>,
421
+ 'queryKey'
422
+ > = {}
416
423
 
417
424
  defaults.forEach((queryDefault) => {
418
425
  if (partialMatchKey(queryKey, queryDefault.queryKey)) {
@@ -456,7 +463,7 @@ export class QueryClient {
456
463
  TQueryKey extends QueryKey = QueryKey,
457
464
  TPageParam = never,
458
465
  >(
459
- options?:
466
+ options:
460
467
  | QueryObserverOptions<
461
468
  TQueryFnData,
462
469
  TError,
@@ -479,7 +486,7 @@ export class QueryClient {
479
486
  TQueryData,
480
487
  TQueryKey
481
488
  > {
482
- if (options?._defaulted) {
489
+ if (options._defaulted) {
483
490
  return options as DefaultedQueryObserverOptions<
484
491
  TQueryFnData,
485
492
  TError,
@@ -491,7 +498,7 @@ export class QueryClient {
491
498
 
492
499
  const defaultedOptions = {
493
500
  ...this.#defaultOptions.queries,
494
- ...(options?.queryKey && this.getQueryDefaults(options.queryKey)),
501
+ ...this.getQueryDefaults(options.queryKey),
495
502
  ...options,
496
503
  _defaulted: true,
497
504
  }
@@ -132,7 +132,7 @@ export class QueryObserver<
132
132
  }
133
133
 
134
134
  setOptions(
135
- options?: QueryObserverOptions<
135
+ options: QueryObserverOptions<
136
136
  TQueryFnData,
137
137
  TError,
138
138
  TData,
@@ -146,7 +146,7 @@ export class QueryObserver<
146
146
 
147
147
  this.options = this.#client.defaultQueryOptions(options)
148
148
 
149
- if (!shallowEqualObjects(prevOptions, this.options)) {
149
+ if (!shallowEqualObjects(this.options, prevOptions)) {
150
150
  this.#client.getQueryCache().notify({
151
151
  type: 'observerOptionsUpdated',
152
152
  query: this.#currentQuery,
@@ -161,11 +161,6 @@ export class QueryObserver<
161
161
  throw new Error('Expected enabled to be a boolean')
162
162
  }
163
163
 
164
- // Keep previous query key if the user does not supply one
165
- if (!this.options.queryKey) {
166
- this.options.queryKey = prevOptions.queryKey
167
- }
168
-
169
164
  this.#updateQuery()
170
165
 
171
166
  const mounted = this.hasListeners()
@@ -34,31 +34,6 @@ describe('queriesObserver', () => {
34
34
  expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }])
35
35
  })
36
36
 
37
- test('should still return value for undefined query key', async () => {
38
- const consoleMock = vi.spyOn(console, 'error')
39
- consoleMock.mockImplementation(() => undefined)
40
- const key1 = queryKey()
41
- const queryFn1 = vi.fn().mockReturnValue(1)
42
- const queryFn2 = vi.fn().mockReturnValue(2)
43
- const observer = new QueriesObserver(queryClient, [
44
- { queryKey: key1, queryFn: queryFn1 },
45
- { queryKey: undefined, queryFn: queryFn2 },
46
- ])
47
- let observerResult
48
- const unsubscribe = observer.subscribe((result) => {
49
- observerResult = result
50
- })
51
- await sleep(1)
52
- unsubscribe()
53
- expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }])
54
-
55
- expect(consoleMock).toHaveBeenCalledTimes(1)
56
- expect(consoleMock).toHaveBeenCalledWith(
57
- "As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']",
58
- )
59
- consoleMock.mockRestore()
60
- })
61
-
62
37
  test('should update when a query updates', async () => {
63
38
  const key1 = queryKey()
64
39
  const key2 = queryKey()
@@ -53,10 +53,11 @@ describe('queryCache', () => {
53
53
  const unsubScribeObserver = observer.subscribe(vi.fn())
54
54
 
55
55
  await waitFor(() => {
56
- expect(events.length).toBe(8)
56
+ expect(events.length).toBe(9)
57
57
  })
58
58
 
59
59
  expect(events).toEqual([
60
+ 'observerOptionsUpdated',
60
61
  'added', // 1. Query added -> loading
61
62
  'observerResultsUpdated', // 2. Observer result updated -> loading
62
63
  'observerAdded', // 3. Observer added
@@ -356,6 +356,16 @@ describe('queryClient', () => {
356
356
  }),
357
357
  )
358
358
  })
359
+
360
+ test('should set 10k data in less than 500ms', () => {
361
+ const key = queryKey()
362
+ const start = performance.now()
363
+ for (let i = 0; i < 10000; i++) {
364
+ queryClient.setQueryData([key, i], i)
365
+ }
366
+ const end = performance.now()
367
+ expect(end - start).toBeLessThan(500)
368
+ })
359
369
  })
360
370
 
361
371
  describe('setQueriesData', () => {
@@ -419,6 +429,38 @@ describe('queryClient', () => {
419
429
  queryClient.setQueryData([key, 'id'], 'bar')
420
430
  expect(queryClient.getQueryData([key])).toBeUndefined()
421
431
  })
432
+
433
+ test('should get 10k queries in less than 500ms', () => {
434
+ const key = queryKey()
435
+ for (let i = 0; i < 10000; i++) {
436
+ queryClient.setQueryData([key, i], i)
437
+ }
438
+
439
+ const start = performance.now()
440
+ for (let i = 0; i < 10000; i++) {
441
+ queryClient.getQueryData([key, i])
442
+ }
443
+ const end = performance.now()
444
+
445
+ expect(end - start).toBeLessThan(500)
446
+ })
447
+ })
448
+
449
+ describe('getQueryState', () => {
450
+ test('should get 10k queries in less than 500ms', () => {
451
+ const key = queryKey()
452
+ for (let i = 0; i < 10000; i++) {
453
+ queryClient.setQueryData([key, i], i)
454
+ }
455
+
456
+ const start = performance.now()
457
+ for (let i = 0; i < 10000; i++) {
458
+ queryClient.getQueryState([key, i])
459
+ }
460
+ const end = performance.now()
461
+
462
+ expect(end - start).toBeLessThan(500)
463
+ })
422
464
  })
423
465
 
424
466
  describe('ensureQueryData', () => {
@@ -459,7 +459,7 @@ describe('queryObserver', () => {
459
459
  const unsubscribe = observer.subscribe((x) => {
460
460
  results.push(x)
461
461
  })
462
- observer.setOptions({ enabled: false, staleTime: 10 })
462
+ observer.setOptions({ queryKey: key, enabled: false, staleTime: 10 })
463
463
  await queryClient.fetchQuery({ queryKey: key, queryFn })
464
464
  await sleep(100)
465
465
  unsubscribe()
@@ -579,7 +579,7 @@ describe('queryObserver', () => {
579
579
 
580
580
  const firstData = observer.getCurrentResult().data
581
581
 
582
- observer.setOptions({ placeholderData: {} })
582
+ observer.setOptions({ queryKey: key, placeholderData: {} })
583
583
 
584
584
  const secondData = observer.getCurrentResult().data
585
585
 
@@ -885,7 +885,7 @@ describe('queryObserver', () => {
885
885
 
886
886
  const spy = vi.fn()
887
887
  const unsubscribe = queryClient.getQueryCache().subscribe(spy)
888
- observer.setOptions({ enabled: false })
888
+ observer.setOptions({ queryKey: key, enabled: false })
889
889
 
890
890
  expect(spy).toHaveBeenCalledTimes(1)
891
891
  expect(spy).toHaveBeenCalledWith(
@@ -31,6 +31,7 @@ describe('placeholderData', () => {
31
31
  }
32
32
 
33
33
  new QueryObserver<boolean, CustomError>(createQueryClient(), {
34
+ queryKey: ['key'],
34
35
  placeholderData: (_, previousQuery) => {
35
36
  const error = previousQuery?.state.error
36
37
 
@@ -49,6 +50,7 @@ describe('placeholderData', () => {
49
50
  type QueryData = typeof queryData
50
51
 
51
52
  new QueryObserver(createQueryClient(), {
53
+ queryKey: ['key'],
52
54
  queryFn: () => queryData,
53
55
  select: (data) => data.foo,
54
56
  placeholderData: (previousData) => {
@@ -7,11 +7,29 @@ import {
7
7
  matchMutation,
8
8
  partialMatchKey,
9
9
  replaceEqualDeep,
10
+ shallowEqualObjects,
10
11
  } from '../utils'
11
12
  import { Mutation } from '../mutation'
12
13
  import { createQueryClient } from './utils'
13
14
 
14
15
  describe('core/utils', () => {
16
+ describe('shallowEqualObjects', () => {
17
+ it('should return `true` for shallow equal objects', () => {
18
+ expect(shallowEqualObjects({ a: 1 }, { a: 1 })).toEqual(true)
19
+ })
20
+
21
+ it('should return `false` for non shallow equal objects', () => {
22
+ expect(shallowEqualObjects({ a: 1 }, { a: 2 })).toEqual(false)
23
+ })
24
+
25
+ it('should return `false` if lengths are not equal', () => {
26
+ expect(shallowEqualObjects({ a: 1 }, { a: 1, b: 2 })).toEqual(false)
27
+ })
28
+
29
+ it('should return false if b is undefined', () => {
30
+ expect(shallowEqualObjects({ a: 1 }, undefined)).toEqual(false)
31
+ })
32
+ })
15
33
  describe('isPlainObject', () => {
16
34
  it('should return `true` for a plain object', () => {
17
35
  expect(isPlainObject({})).toEqual(true)
package/src/types.ts CHANGED
@@ -218,12 +218,9 @@ export interface QueryObserverOptions<
218
218
  TQueryData = TQueryFnData,
219
219
  TQueryKey extends QueryKey = QueryKey,
220
220
  TPageParam = never,
221
- > extends QueryOptions<
222
- TQueryFnData,
223
- TError,
224
- TQueryData,
225
- TQueryKey,
226
- TPageParam
221
+ > extends WithRequired<
222
+ QueryOptions<TQueryFnData, TError, TQueryData, TQueryKey, TPageParam>,
223
+ 'queryKey'
227
224
  > {
228
225
  /**
229
226
  * Set this to `false` to disable automatic refetching when the query mounts or changes query keys.
@@ -340,6 +337,11 @@ export interface QueryObserverOptions<
340
337
  export type WithRequired<TTarget, TKey extends keyof TTarget> = TTarget & {
341
338
  [_ in TKey]: {}
342
339
  }
340
+ export type Optional<TTarget, TKey extends keyof TTarget> = Pick<
341
+ Partial<TTarget>,
342
+ TKey
343
+ > &
344
+ Omit<TTarget, TKey>
343
345
 
344
346
  export type DefaultedQueryObserverOptions<
345
347
  TQueryFnData = unknown,
@@ -871,7 +873,7 @@ export interface QueryClientConfig {
871
873
  }
872
874
 
873
875
  export interface DefaultOptions<TError = DefaultError> {
874
- queries?: Omit<QueryObserverOptions<unknown, TError>, 'suspense'>
876
+ queries?: Omit<QueryObserverOptions<unknown, TError>, 'suspense' | 'queryKey'>
875
877
  mutations?: MutationObserverOptions<unknown, TError, unknown, unknown>
876
878
  }
877
879
 
package/src/utils.ts CHANGED
@@ -167,7 +167,7 @@ export function matchMutation(
167
167
 
168
168
  export function hashQueryKeyByOptions<TQueryKey extends QueryKey = QueryKey>(
169
169
  queryKey: TQueryKey,
170
- options?: QueryOptions<any, any, any, TQueryKey>,
170
+ options?: Pick<QueryOptions<any, any, any, any>, 'queryKeyHashFn'>,
171
171
  ): string {
172
172
  const hashFn = options?.queryKeyHashFn || hashKey
173
173
  return hashFn(queryKey)
@@ -257,10 +257,13 @@ export function replaceEqualDeep(a: any, b: any): any {
257
257
  }
258
258
 
259
259
  /**
260
- * Shallow compare objects. Only works with objects that always have the same properties.
260
+ * Shallow compare objects.
261
261
  */
262
- export function shallowEqualObjects<T>(a: T, b: T): boolean {
263
- if ((a && !b) || (b && !a)) {
262
+ export function shallowEqualObjects<T extends Record<string, any>>(
263
+ a: T,
264
+ b: T | undefined,
265
+ ): boolean {
266
+ if (!b || Object.keys(a).length !== Object.keys(b).length) {
264
267
  return false
265
268
  }
266
269