@tanstack/query-core 5.56.2 → 5.59.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.
Files changed (110) hide show
  1. package/build/legacy/{hydration-D6canbuP.d.cts → hydration-CVKA21uy.d.cts} +53 -0
  2. package/build/legacy/{hydration-DgUcX5FN.d.ts → hydration-DHZNyHqg.d.ts} +53 -0
  3. package/build/legacy/hydration.d.cts +1 -1
  4. package/build/legacy/hydration.d.ts +1 -1
  5. package/build/legacy/index.d.cts +1 -1
  6. package/build/legacy/index.d.ts +1 -1
  7. package/build/legacy/infiniteQueryBehavior.d.cts +1 -1
  8. package/build/legacy/infiniteQueryBehavior.d.ts +1 -1
  9. package/build/legacy/infiniteQueryObserver.d.cts +1 -1
  10. package/build/legacy/infiniteQueryObserver.d.ts +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.d.cts +1 -1
  16. package/build/legacy/mutationObserver.d.ts +1 -1
  17. package/build/legacy/queriesObserver.cjs +19 -8
  18. package/build/legacy/queriesObserver.cjs.map +1 -1
  19. package/build/legacy/queriesObserver.d.cts +3 -3
  20. package/build/legacy/queriesObserver.d.ts +3 -3
  21. package/build/legacy/queriesObserver.js +19 -8
  22. package/build/legacy/queriesObserver.js.map +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.d.cts +1 -1
  26. package/build/legacy/queryCache.d.ts +1 -1
  27. package/build/legacy/queryClient.d.cts +1 -1
  28. package/build/legacy/queryClient.d.ts +1 -1
  29. package/build/legacy/queryObserver.cjs +44 -5
  30. package/build/legacy/queryObserver.cjs.map +1 -1
  31. package/build/legacy/queryObserver.d.cts +1 -1
  32. package/build/legacy/queryObserver.d.ts +1 -1
  33. package/build/legacy/queryObserver.js +45 -6
  34. package/build/legacy/queryObserver.js.map +1 -1
  35. package/build/legacy/retryer.cjs +7 -11
  36. package/build/legacy/retryer.cjs.map +1 -1
  37. package/build/legacy/retryer.d.cts +1 -1
  38. package/build/legacy/retryer.d.ts +1 -1
  39. package/build/legacy/retryer.js +7 -11
  40. package/build/legacy/retryer.js.map +1 -1
  41. package/build/legacy/thenable.cjs +61 -0
  42. package/build/legacy/thenable.cjs.map +1 -0
  43. package/build/legacy/thenable.d.cts +36 -0
  44. package/build/legacy/thenable.d.ts +36 -0
  45. package/build/legacy/thenable.js +38 -0
  46. package/build/legacy/thenable.js.map +1 -0
  47. package/build/legacy/types.cjs.map +1 -1
  48. package/build/legacy/types.d.cts +1 -1
  49. package/build/legacy/types.d.ts +1 -1
  50. package/build/legacy/utils.d.cts +1 -1
  51. package/build/legacy/utils.d.ts +1 -1
  52. package/build/modern/{hydration-D6canbuP.d.cts → hydration-CVKA21uy.d.cts} +53 -0
  53. package/build/modern/{hydration-DgUcX5FN.d.ts → hydration-DHZNyHqg.d.ts} +53 -0
  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.d.cts +1 -1
  61. package/build/modern/infiniteQueryObserver.d.ts +1 -1
  62. package/build/modern/mutation.d.cts +1 -1
  63. package/build/modern/mutation.d.ts +1 -1
  64. package/build/modern/mutationCache.d.cts +1 -1
  65. package/build/modern/mutationCache.d.ts +1 -1
  66. package/build/modern/mutationObserver.d.cts +1 -1
  67. package/build/modern/mutationObserver.d.ts +1 -1
  68. package/build/modern/queriesObserver.cjs +19 -7
  69. package/build/modern/queriesObserver.cjs.map +1 -1
  70. package/build/modern/queriesObserver.d.cts +3 -3
  71. package/build/modern/queriesObserver.d.ts +3 -3
  72. package/build/modern/queriesObserver.js +19 -7
  73. package/build/modern/queriesObserver.js.map +1 -1
  74. package/build/modern/query.d.cts +1 -1
  75. package/build/modern/query.d.ts +1 -1
  76. package/build/modern/queryCache.d.cts +1 -1
  77. package/build/modern/queryCache.d.ts +1 -1
  78. package/build/modern/queryClient.d.cts +1 -1
  79. package/build/modern/queryClient.d.ts +1 -1
  80. package/build/modern/queryObserver.cjs +42 -4
  81. package/build/modern/queryObserver.cjs.map +1 -1
  82. package/build/modern/queryObserver.d.cts +1 -1
  83. package/build/modern/queryObserver.d.ts +1 -1
  84. package/build/modern/queryObserver.js +43 -5
  85. package/build/modern/queryObserver.js.map +1 -1
  86. package/build/modern/retryer.cjs +7 -11
  87. package/build/modern/retryer.cjs.map +1 -1
  88. package/build/modern/retryer.d.cts +1 -1
  89. package/build/modern/retryer.d.ts +1 -1
  90. package/build/modern/retryer.js +7 -11
  91. package/build/modern/retryer.js.map +1 -1
  92. package/build/modern/thenable.cjs +61 -0
  93. package/build/modern/thenable.cjs.map +1 -0
  94. package/build/modern/thenable.d.cts +36 -0
  95. package/build/modern/thenable.d.ts +36 -0
  96. package/build/modern/thenable.js +36 -0
  97. package/build/modern/thenable.js.map +1 -0
  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.d.cts +1 -1
  102. package/build/modern/utils.d.ts +1 -1
  103. package/package.json +1 -1
  104. package/src/__tests__/hydration.test.tsx +38 -0
  105. package/src/__tests__/queryObserver.test.tsx +101 -1
  106. package/src/queriesObserver.ts +20 -7
  107. package/src/queryObserver.ts +63 -4
  108. package/src/retryer.ts +7 -11
  109. package/src/thenable.ts +82 -0
  110. package/src/types.ts +54 -0
@@ -16,7 +16,13 @@ describe('queryObserver', () => {
16
16
  let queryClient: QueryClient
17
17
 
18
18
  beforeEach(() => {
19
- queryClient = createQueryClient()
19
+ queryClient = createQueryClient({
20
+ defaultOptions: {
21
+ queries: {
22
+ experimental_prefetchInRender: true,
23
+ },
24
+ },
25
+ })
20
26
  queryClient.mount()
21
27
  })
22
28
 
@@ -1133,4 +1139,98 @@ describe('queryObserver', () => {
1133
1139
 
1134
1140
  unsubscribe()
1135
1141
  })
1142
+
1143
+ test('should return a promise that resolves when data is present', async () => {
1144
+ const results: Array<QueryObserverResult> = []
1145
+ const key = queryKey()
1146
+ let count = 0
1147
+ const observer = new QueryObserver(queryClient, {
1148
+ queryKey: key,
1149
+ queryFn: () => {
1150
+ if (++count > 9) {
1151
+ return Promise.resolve('data')
1152
+ }
1153
+ throw new Error('rejected')
1154
+ },
1155
+ retry: 10,
1156
+ retryDelay: 0,
1157
+ })
1158
+ const unsubscribe = observer.subscribe(() => {
1159
+ results.push(observer.getCurrentResult())
1160
+ })
1161
+
1162
+ await waitFor(() => {
1163
+ expect(results.at(-1)?.data).toBe('data')
1164
+ })
1165
+
1166
+ const numberOfUniquePromises = new Set(
1167
+ results.map((result) => result.promise),
1168
+ ).size
1169
+ expect(numberOfUniquePromises).toBe(1)
1170
+
1171
+ unsubscribe()
1172
+ })
1173
+
1174
+ test('should return a new promise after recovering from an error', async () => {
1175
+ const results: Array<QueryObserverResult> = []
1176
+ const key = queryKey()
1177
+
1178
+ let succeeds = false
1179
+ let idx = 0
1180
+ const observer = new QueryObserver(queryClient, {
1181
+ queryKey: key,
1182
+ queryFn: () => {
1183
+ if (succeeds) {
1184
+ return Promise.resolve('data')
1185
+ }
1186
+ throw new Error(`rejected #${++idx}`)
1187
+ },
1188
+ retry: 5,
1189
+ retryDelay: 0,
1190
+ })
1191
+ const unsubscribe = observer.subscribe(() => {
1192
+ results.push(observer.getCurrentResult())
1193
+ })
1194
+
1195
+ await waitFor(() => {
1196
+ expect(results.at(-1)?.status).toBe('error')
1197
+ })
1198
+
1199
+ expect(
1200
+ results.every((result) => result.promise === results[0]!.promise),
1201
+ ).toBe(true)
1202
+
1203
+ {
1204
+ // fail again
1205
+ const lengthBefore = results.length
1206
+ observer.refetch()
1207
+ await waitFor(() => {
1208
+ expect(results.length).toBeGreaterThan(lengthBefore)
1209
+ expect(results.at(-1)?.status).toBe('error')
1210
+ })
1211
+
1212
+ const numberOfUniquePromises = new Set(
1213
+ results.map((result) => result.promise),
1214
+ ).size
1215
+
1216
+ expect(numberOfUniquePromises).toBe(2)
1217
+ }
1218
+ {
1219
+ // succeed
1220
+ succeeds = true
1221
+ observer.refetch()
1222
+
1223
+ await waitFor(() => {
1224
+ results.at(-1)?.status === 'success'
1225
+ })
1226
+
1227
+ const numberOfUniquePromises = new Set(
1228
+ results.map((result) => result.promise),
1229
+ ).size
1230
+
1231
+ expect(numberOfUniquePromises).toBe(3)
1232
+ }
1233
+
1234
+ unsubscribe()
1235
+ })
1136
1236
  })
@@ -38,6 +38,7 @@ export class QueriesObserver<
38
38
  #client: QueryClient
39
39
  #result!: Array<QueryObserverResult>
40
40
  #queries: Array<QueryObserverOptions>
41
+ #options?: QueriesObserverOptions<TCombinedResult>
41
42
  #observers: Array<QueryObserver>
42
43
  #combinedResult?: TCombinedResult
43
44
  #lastCombine?: CombineFn<TCombinedResult>
@@ -46,11 +47,12 @@ export class QueriesObserver<
46
47
  constructor(
47
48
  client: QueryClient,
48
49
  queries: Array<QueryObserverOptions<any, any, any, any, any>>,
49
- _options?: QueriesObserverOptions<TCombinedResult>,
50
+ options?: QueriesObserverOptions<TCombinedResult>,
50
51
  ) {
51
52
  super()
52
53
 
53
54
  this.#client = client
55
+ this.#options = options
54
56
  this.#queries = []
55
57
  this.#observers = []
56
58
  this.#result = []
@@ -83,10 +85,11 @@ export class QueriesObserver<
83
85
 
84
86
  setQueries(
85
87
  queries: Array<QueryObserverOptions>,
86
- _options?: QueriesObserverOptions<TCombinedResult>,
88
+ options?: QueriesObserverOptions<TCombinedResult>,
87
89
  notifyOptions?: NotifyOptions,
88
90
  ): void {
89
91
  this.#queries = queries
92
+ this.#options = options
90
93
 
91
94
  notifyManager.batch(() => {
92
95
  const prevObservers = this.#observers
@@ -268,11 +271,21 @@ export class QueriesObserver<
268
271
  }
269
272
 
270
273
  #notify(): void {
271
- notifyManager.batch(() => {
272
- this.listeners.forEach((listener) => {
273
- listener(this.#result)
274
- })
275
- })
274
+ if (this.hasListeners()) {
275
+ const previousResult = this.#combinedResult
276
+ const newResult = this.#combineResult(
277
+ this.#result,
278
+ this.#options?.combine,
279
+ )
280
+
281
+ if (previousResult !== newResult) {
282
+ notifyManager.batch(() => {
283
+ this.listeners.forEach((listener) => {
284
+ listener(this.#result)
285
+ })
286
+ })
287
+ }
288
+ }
276
289
  }
277
290
  }
278
291
 
@@ -1,3 +1,8 @@
1
+ import { focusManager } from './focusManager'
2
+ import { notifyManager } from './notifyManager'
3
+ import { fetchState } from './query'
4
+ import { Subscribable } from './subscribable'
5
+ import { pendingThenable } from './thenable'
1
6
  import {
2
7
  isServer,
3
8
  isValidTimeout,
@@ -8,12 +13,9 @@ import {
8
13
  shallowEqualObjects,
9
14
  timeUntilStale,
10
15
  } from './utils'
11
- import { notifyManager } from './notifyManager'
12
- import { focusManager } from './focusManager'
13
- import { Subscribable } from './subscribable'
14
- import { fetchState } from './query'
15
16
  import type { FetchOptions, Query, QueryState } from './query'
16
17
  import type { QueryClient } from './queryClient'
18
+ import type { PendingThenable, Thenable } from './thenable'
17
19
  import type {
18
20
  DefaultError,
19
21
  DefaultedQueryObserverOptions,
@@ -57,6 +59,7 @@ export class QueryObserver<
57
59
  TQueryData,
58
60
  TQueryKey
59
61
  >
62
+ #currentThenable: Thenable<TData>
60
63
  #selectError: TError | null
61
64
  #selectFn?: (data: TQueryData) => TData
62
65
  #selectResult?: TData
@@ -82,6 +85,13 @@ export class QueryObserver<
82
85
 
83
86
  this.#client = client
84
87
  this.#selectError = null
88
+ this.#currentThenable = pendingThenable()
89
+ if (!this.options.experimental_prefetchInRender) {
90
+ this.#currentThenable.reject(
91
+ new Error('experimental_prefetchInRender feature flag is not enabled'),
92
+ )
93
+ }
94
+
85
95
  this.bindMethods()
86
96
  this.setOptions(options)
87
97
  }
@@ -582,6 +592,7 @@ export class QueryObserver<
582
592
  isRefetchError: isError && hasData,
583
593
  isStale: isStale(query, options),
584
594
  refetch: this.refetch,
595
+ promise: this.#currentThenable,
585
596
  }
586
597
 
587
598
  return result as QueryObserverResult<TData, TError>
@@ -593,6 +604,7 @@ export class QueryObserver<
593
604
  | undefined
594
605
 
595
606
  const nextResult = this.createResult(this.#currentQuery, this.options)
607
+
596
608
  this.#currentResultState = this.#currentQuery.state
597
609
  this.#currentResultOptions = this.options
598
610
 
@@ -605,6 +617,52 @@ export class QueryObserver<
605
617
  return
606
618
  }
607
619
 
620
+ if (this.options.experimental_prefetchInRender) {
621
+ const finalizeThenableIfPossible = (thenable: PendingThenable<TData>) => {
622
+ if (nextResult.status === 'error') {
623
+ thenable.reject(nextResult.error)
624
+ } else if (nextResult.data !== undefined) {
625
+ thenable.resolve(nextResult.data)
626
+ }
627
+ }
628
+
629
+ /**
630
+ * Create a new thenable and result promise when the results have changed
631
+ */
632
+ const recreateThenable = () => {
633
+ const pending =
634
+ (this.#currentThenable =
635
+ nextResult.promise =
636
+ pendingThenable())
637
+
638
+ finalizeThenableIfPossible(pending)
639
+ }
640
+
641
+ const prevThenable = this.#currentThenable
642
+ switch (prevThenable.status) {
643
+ case 'pending':
644
+ // Finalize the previous thenable if it was pending
645
+ finalizeThenableIfPossible(prevThenable)
646
+ break
647
+ case 'fulfilled':
648
+ if (
649
+ nextResult.status === 'error' ||
650
+ nextResult.data !== prevThenable.value
651
+ ) {
652
+ recreateThenable()
653
+ }
654
+ break
655
+ case 'rejected':
656
+ if (
657
+ nextResult.status !== 'error' ||
658
+ nextResult.error !== prevThenable.reason
659
+ ) {
660
+ recreateThenable()
661
+ }
662
+ break
663
+ }
664
+ }
665
+
608
666
  this.#currentResult = nextResult
609
667
 
610
668
  // Determine which callbacks to trigger
@@ -639,6 +697,7 @@ export class QueryObserver<
639
697
  return Object.keys(this.#currentResult).some((key) => {
640
698
  const typedKey = key as keyof QueryObserverResult
641
699
  const changed = this.#currentResult[typedKey] !== prevResult[typedKey]
700
+
642
701
  return changed && includedProps.has(typedKey)
643
702
  })
644
703
  }
package/src/retryer.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { focusManager } from './focusManager'
2
2
  import { onlineManager } from './onlineManager'
3
+ import { pendingThenable } from './thenable'
3
4
  import { isServer, sleep } from './utils'
4
5
  import type { CancelOptions, DefaultError, NetworkMode } from './types'
5
6
 
@@ -75,13 +76,8 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
75
76
  let failureCount = 0
76
77
  let isResolved = false
77
78
  let continueFn: ((value?: unknown) => void) | undefined
78
- let promiseResolve: (data: TData) => void
79
- let promiseReject: (error: TError) => void
80
79
 
81
- const promise = new Promise<TData>((outerResolve, outerReject) => {
82
- promiseResolve = outerResolve
83
- promiseReject = outerReject
84
- })
80
+ const thenable = pendingThenable<TData>()
85
81
 
86
82
  const cancel = (cancelOptions?: CancelOptions): void => {
87
83
  if (!isResolved) {
@@ -110,7 +106,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
110
106
  isResolved = true
111
107
  config.onSuccess?.(value)
112
108
  continueFn?.()
113
- promiseResolve(value)
109
+ thenable.resolve(value)
114
110
  }
115
111
  }
116
112
 
@@ -119,7 +115,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
119
115
  isResolved = true
120
116
  config.onError?.(value)
121
117
  continueFn?.()
122
- promiseReject(value)
118
+ thenable.reject(value)
123
119
  }
124
120
  }
125
121
 
@@ -207,11 +203,11 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
207
203
  }
208
204
 
209
205
  return {
210
- promise,
206
+ promise: thenable,
211
207
  cancel,
212
208
  continue: () => {
213
209
  continueFn?.()
214
- return promise
210
+ return thenable
215
211
  },
216
212
  cancelRetry,
217
213
  continueRetry,
@@ -223,7 +219,7 @@ export function createRetryer<TData = unknown, TError = DefaultError>(
223
219
  } else {
224
220
  pause().then(run)
225
221
  }
226
- return promise
222
+ return thenable
227
223
  },
228
224
  }
229
225
  }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Thenable types which matches React's types for promises
3
+ *
4
+ * React seemingly uses `.status`, `.value` and `.reason` properties on a promises to optimistically unwrap data from promises
5
+ *
6
+ * @see https://github.com/facebook/react/blob/main/packages/shared/ReactTypes.js#L112-L138
7
+ * @see https://github.com/facebook/react/blob/4f604941569d2e8947ce1460a0b2997e835f37b9/packages/react-debug-tools/src/ReactDebugHooks.js#L224-L227
8
+ */
9
+
10
+ interface Fulfilled<T> {
11
+ status: 'fulfilled'
12
+ value: T
13
+ }
14
+ interface Rejected {
15
+ status: 'rejected'
16
+ reason: unknown
17
+ }
18
+ interface Pending<T> {
19
+ status: 'pending'
20
+
21
+ /**
22
+ * Resolve the promise with a value.
23
+ * Will remove the `resolve` and `reject` properties from the promise.
24
+ */
25
+ resolve: (value: T) => void
26
+ /**
27
+ * Reject the promise with a reason.
28
+ * Will remove the `resolve` and `reject` properties from the promise.
29
+ */
30
+ reject: (reason: unknown) => void
31
+ }
32
+
33
+ export type FulfilledThenable<T> = Promise<T> & Fulfilled<T>
34
+ export type RejectedThenable<T> = Promise<T> & Rejected
35
+ export type PendingThenable<T> = Promise<T> & Pending<T>
36
+
37
+ export type Thenable<T> =
38
+ | FulfilledThenable<T>
39
+ | RejectedThenable<T>
40
+ | PendingThenable<T>
41
+
42
+ export function pendingThenable<T>(): PendingThenable<T> {
43
+ let resolve: Pending<T>['resolve']
44
+ let reject: Pending<T>['reject']
45
+ // this could use `Promise.withResolvers()` in the future
46
+ const thenable = new Promise((_resolve, _reject) => {
47
+ resolve = _resolve
48
+ reject = _reject
49
+ }) as PendingThenable<T>
50
+
51
+ thenable.status = 'pending'
52
+ thenable.catch(() => {
53
+ // prevent unhandled rejection errors
54
+ })
55
+
56
+ function finalize(data: Fulfilled<T> | Rejected) {
57
+ Object.assign(thenable, data)
58
+
59
+ // clear pending props props to avoid calling them twice
60
+ delete (thenable as Partial<PendingThenable<T>>).resolve
61
+ delete (thenable as Partial<PendingThenable<T>>).reject
62
+ }
63
+
64
+ thenable.resolve = (value) => {
65
+ finalize({
66
+ status: 'fulfilled',
67
+ value,
68
+ })
69
+
70
+ resolve(value)
71
+ }
72
+ thenable.reject = (reason) => {
73
+ finalize({
74
+ status: 'rejected',
75
+ reason,
76
+ })
77
+
78
+ reject(reason)
79
+ }
80
+
81
+ return thenable
82
+ }
package/src/types.ts CHANGED
@@ -380,6 +380,11 @@ export interface QueryObserverOptions<
380
380
  >
381
381
 
382
382
  _optimisticResults?: 'optimistic' | 'isRestoring'
383
+
384
+ /**
385
+ * Enable prefetching during rendering
386
+ */
387
+ experimental_prefetchInRender?: boolean
383
388
  }
384
389
 
385
390
  export type WithRequired<TTarget, TKey extends keyof TTarget> = TTarget & {
@@ -687,6 +692,55 @@ export interface QueryObserverBaseResult<
687
692
  * - See [Network Mode](https://tanstack.com/query/latest/docs/framework/react/guides/network-mode) for more information.
688
693
  */
689
694
  fetchStatus: FetchStatus
695
+ /**
696
+ * A stable promise that will be resolved with the data of the query.
697
+ * Requires the `experimental_prefetchInRender` feature flag to be enabled.
698
+ * @example
699
+ *
700
+ * ### Enabling the feature flag
701
+ * ```ts
702
+ * const client = new QueryClient({
703
+ * defaultOptions: {
704
+ * queries: {
705
+ * experimental_prefetchInRender: true,
706
+ * },
707
+ * },
708
+ * })
709
+ * ```
710
+ *
711
+ * ### Usage
712
+ * ```tsx
713
+ * import { useQuery } from '@tanstack/react-query'
714
+ * import React from 'react'
715
+ * import { fetchTodos, type Todo } from './api'
716
+ *
717
+ * function TodoList({ query }: { query: UseQueryResult<Todo[], Error> }) {
718
+ * const data = React.use(query.promise)
719
+ *
720
+ * return (
721
+ * <ul>
722
+ * {data.map(todo => (
723
+ * <li key={todo.id}>{todo.title}</li>
724
+ * ))}
725
+ * </ul>
726
+ * )
727
+ * }
728
+ *
729
+ * export function App() {
730
+ * const query = useQuery({ queryKey: ['todos'], queryFn: fetchTodos })
731
+ *
732
+ * return (
733
+ * <>
734
+ * <h1>Todos</h1>
735
+ * <React.Suspense fallback={<div>Loading...</div>}>
736
+ * <TodoList query={query} />
737
+ * </React.Suspense>
738
+ * </>
739
+ * )
740
+ * }
741
+ * ```
742
+ */
743
+ promise: Promise<TData>
690
744
  }
691
745
 
692
746
  export interface QueryObserverPendingResult<