@tanstack/query-core 4.0.11-beta.0 → 4.0.11-beta.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-core",
3
- "version": "4.0.11-beta.0",
3
+ "version": "4.0.11-beta.1",
4
4
  "description": "TODO",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -48,6 +48,12 @@ interface NotifyEventMutationObserverRemoved {
48
48
  observer: MutationObserver<any, any, any>
49
49
  }
50
50
 
51
+ interface NotifyEventMutationObserverOptionsUpdated {
52
+ type: 'observerOptionsUpdated'
53
+ mutation?: Mutation<any, any, any, any>
54
+ observer: MutationObserver<any, any, any, any>
55
+ }
56
+
51
57
  interface NotifyEventMutationUpdated {
52
58
  type: 'updated'
53
59
  mutation: Mutation<any, any, any, any>
@@ -59,6 +65,7 @@ type MutationCacheNotifyEvent =
59
65
  | NotifyEventMutationRemoved
60
66
  | NotifyEventMutationObserverAdded
61
67
  | NotifyEventMutationObserverRemoved
68
+ | NotifyEventMutationObserverOptionsUpdated
62
69
  | NotifyEventMutationUpdated
63
70
 
64
71
  type MutationCacheListener = (event: MutationCacheNotifyEvent) => void
@@ -8,6 +8,7 @@ import type {
8
8
  MutationObserverResult,
9
9
  MutationObserverOptions,
10
10
  } from './types'
11
+ import { shallowEqualObjects } from './utils'
11
12
 
12
13
  // TYPES
13
14
 
@@ -63,7 +64,15 @@ export class MutationObserver<
63
64
  setOptions(
64
65
  options?: MutationObserverOptions<TData, TError, TVariables, TContext>,
65
66
  ) {
67
+ const prevOptions = this.options
66
68
  this.options = this.client.defaultMutationOptions(options)
69
+ if (!shallowEqualObjects(prevOptions, this.options)) {
70
+ this.client.getMutationCache().notify({
71
+ type: 'observerOptionsUpdated',
72
+ mutation: this.currentMutation,
73
+ observer: this,
74
+ })
75
+ }
67
76
  }
68
77
 
69
78
  protected onUnsubscribe(): void {
package/src/queryCache.ts CHANGED
@@ -55,6 +55,12 @@ interface NotifyEventQueryObserverResultsUpdated {
55
55
  query: Query<any, any, any, any>
56
56
  }
57
57
 
58
+ interface NotifyEventQueryObserverOptionsUpdated {
59
+ type: 'observerOptionsUpdated'
60
+ query: Query<any, any, any, any>
61
+ observer: QueryObserver<any, any, any, any, any>
62
+ }
63
+
58
64
  type QueryCacheNotifyEvent =
59
65
  | NotifyEventQueryAdded
60
66
  | NotifyEventQueryRemoved
@@ -62,6 +68,7 @@ type QueryCacheNotifyEvent =
62
68
  | NotifyEventQueryObserverAdded
63
69
  | NotifyEventQueryObserverRemoved
64
70
  | NotifyEventQueryObserverResultsUpdated
71
+ | NotifyEventQueryObserverOptionsUpdated
65
72
 
66
73
  type QueryCacheListener = (event: QueryCacheNotifyEvent) => void
67
74
 
@@ -155,6 +155,14 @@ export class QueryObserver<
155
155
 
156
156
  this.options = this.client.defaultQueryOptions(options)
157
157
 
158
+ if (!shallowEqualObjects(prevOptions, this.options)) {
159
+ this.client.getQueryCache().notify({
160
+ type: 'observerOptionsUpdated',
161
+ query: this.currentQuery,
162
+ observer: this,
163
+ })
164
+ }
165
+
158
166
  if (
159
167
  typeof this.options.enabled !== 'undefined' &&
160
168
  typeof this.options.enabled !== 'boolean'
@@ -13,6 +13,7 @@ import {
13
13
  QueryClient,
14
14
  QueryFunction,
15
15
  QueryObserver,
16
+ QueryObserverOptions,
16
17
  } from '..'
17
18
  import { focusManager, onlineManager } from '..'
18
19
 
@@ -357,6 +358,38 @@ describe('queryClient', () => {
357
358
  expect(queryCache.find(key)!.state.data).toBe(newData)
358
359
  })
359
360
 
361
+ test('should apply a custom structuralSharing function when provided', () => {
362
+ const key = queryKey()
363
+
364
+ const queryObserverOptions = {
365
+ structuralSharing: (
366
+ prevData: { value: Date } | undefined,
367
+ newData: { value: Date },
368
+ ) => {
369
+ if (!prevData) {
370
+ return newData
371
+ }
372
+ return newData.value.getTime() === prevData.value.getTime()
373
+ ? prevData
374
+ : newData
375
+ },
376
+ } as QueryObserverOptions
377
+
378
+ queryClient.setDefaultOptions({ queries: queryObserverOptions })
379
+
380
+ const oldData = { value: new Date(2022, 6, 19) }
381
+ const newData = { value: new Date(2022, 6, 19) }
382
+ queryClient.setQueryData(key, oldData)
383
+ queryClient.setQueryData(key, newData)
384
+
385
+ expect(queryCache.find(key)!.state.data).toBe(oldData)
386
+
387
+ const distinctData = { value: new Date(2021, 11, 25) }
388
+ queryClient.setQueryData(key, distinctData)
389
+
390
+ expect(queryCache.find(key)!.state.data).toBe(distinctData)
391
+ })
392
+
360
393
  test('should not set isFetching to false', async () => {
361
394
  const key = queryKey()
362
395
  queryClient.prefetchQuery(key, async () => {
@@ -799,4 +799,23 @@ describe('queryObserver', () => {
799
799
 
800
800
  unsubscribe()
801
801
  })
802
+
803
+ test('setOptions should notify cache listeners', async () => {
804
+ const key = queryKey()
805
+
806
+ const observer = new QueryObserver(queryClient, {
807
+ queryKey: key,
808
+ })
809
+
810
+ const spy = jest.fn()
811
+ const unsubscribe = queryClient.getQueryCache().subscribe(spy)
812
+ observer.setOptions({ enabled: false })
813
+
814
+ expect(spy).toHaveBeenCalledTimes(1)
815
+ expect(spy).toHaveBeenCalledWith(
816
+ expect.objectContaining({ type: 'observerOptionsUpdated' }),
817
+ )
818
+
819
+ unsubscribe()
820
+ })
802
821
  })
package/src/types.ts CHANGED
@@ -76,9 +76,12 @@ export interface QueryOptions<
76
76
  behavior?: QueryBehavior<TQueryFnData, TError, TData>
77
77
  /**
78
78
  * Set this to `false` to disable structural sharing between query results.
79
+ * Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic.
79
80
  * Defaults to `true`.
80
81
  */
81
- structuralSharing?: boolean
82
+ structuralSharing?:
83
+ | boolean
84
+ | ((oldData: TData | undefined, newData: TData) => TData)
82
85
  /**
83
86
  * This function can be set to automatically get the previous cursor for infinite queries.
84
87
  * The result will also be used to determine the value of `hasPreviousPage`.
@@ -425,11 +428,14 @@ export interface QueryObserverSuccessResult<TData = unknown, TError = unknown>
425
428
  status: 'success'
426
429
  }
427
430
 
431
+ export type DefinedQueryObserverResult<TData = unknown, TError = unknown> =
432
+ | QueryObserverRefetchErrorResult<TData, TError>
433
+ | QueryObserverSuccessResult<TData, TError>
434
+
428
435
  export type QueryObserverResult<TData = unknown, TError = unknown> =
436
+ | DefinedQueryObserverResult<TData, TError>
429
437
  | QueryObserverLoadingErrorResult<TData, TError>
430
438
  | QueryObserverLoadingResult<TData, TError>
431
- | QueryObserverRefetchErrorResult<TData, TError>
432
- | QueryObserverSuccessResult<TData, TError>
433
439
 
434
440
  export interface InfiniteQueryObserverBaseResult<
435
441
  TData = unknown,
@@ -535,18 +541,18 @@ export interface MutationOptions<
535
541
  data: TData,
536
542
  variables: TVariables,
537
543
  context: TContext | undefined,
538
- ) => Promise<unknown> | void
544
+ ) => Promise<unknown> | unknown
539
545
  onError?: (
540
546
  error: TError,
541
547
  variables: TVariables,
542
548
  context: TContext | undefined,
543
- ) => Promise<unknown> | void
549
+ ) => Promise<unknown> | unknown
544
550
  onSettled?: (
545
551
  data: TData | undefined,
546
552
  error: TError | null,
547
553
  variables: TVariables,
548
554
  context: TContext | undefined,
549
- ) => Promise<unknown> | void
555
+ ) => Promise<unknown> | unknown
550
556
  retry?: RetryValue<TError>
551
557
  retryDelay?: RetryDelayValue<TError>
552
558
  networkMode?: NetworkMode
@@ -574,18 +580,18 @@ export interface MutateOptions<
574
580
  data: TData,
575
581
  variables: TVariables,
576
582
  context: TContext,
577
- ) => Promise<unknown> | void
583
+ ) => Promise<unknown> | unknown
578
584
  onError?: (
579
585
  error: TError,
580
586
  variables: TVariables,
581
587
  context: TContext | undefined,
582
- ) => Promise<unknown> | void
588
+ ) => Promise<unknown> | unknown
583
589
  onSettled?: (
584
590
  data: TData | undefined,
585
591
  error: TError | null,
586
592
  variables: TVariables,
587
593
  context: TContext | undefined,
588
- ) => Promise<unknown> | void
594
+ ) => Promise<unknown> | unknown
589
595
  }
590
596
 
591
597
  export type MutateFunction<
package/src/utils.ts CHANGED
@@ -427,6 +427,8 @@ export function replaceData<
427
427
  // Use prev data if an isDataEqual function is defined and returns `true`
428
428
  if (options.isDataEqual?.(prevData, data)) {
429
429
  return prevData as TData
430
+ } else if (typeof options.structuralSharing === 'function') {
431
+ return options.structuralSharing(prevData, data)
430
432
  } else if (options.structuralSharing !== false) {
431
433
  // Structurally share data between prev and new data if needed
432
434
  return replaceEqualDeep(prevData, data)