Spaces:
Paused
Paused
File size: 5,228 Bytes
2eb1c4f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 'use client'
import * as React from 'react'
import { isServer, noop, notifyManager } from '@tanstack/query-core'
import { useQueryClient } from './QueryClientProvider'
import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary'
import {
ensurePreventErrorBoundaryRetry,
getHasError,
useClearResetErrorBoundary,
} from './errorBoundaryUtils'
import { useIsRestoring } from './IsRestoringProvider'
import {
ensureSuspenseTimers,
fetchOptimistic,
shouldSuspend,
willFetch,
} from './suspense'
import type {
QueryClient,
QueryKey,
QueryObserver,
QueryObserverResult,
} from '@tanstack/query-core'
import type { UseBaseQueryOptions } from './types'
export function useBaseQuery<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey extends QueryKey,
>(
options: UseBaseQueryOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>,
Observer: typeof QueryObserver,
queryClient?: QueryClient,
): QueryObserverResult<TData, TError> {
if (process.env.NODE_ENV !== 'production') {
if (typeof options !== 'object' || Array.isArray(options)) {
throw new Error(
'Bad argument type. Starting with v5, only the "Object" form is allowed when calling query related functions. Please use the error stack to find the culprit call. More info here: https://tanstack.com/query/latest/docs/react/guides/migrating-to-v5#supports-a-single-signature-one-object',
)
}
}
const isRestoring = useIsRestoring()
const errorResetBoundary = useQueryErrorResetBoundary()
const client = useQueryClient(queryClient)
const defaultedOptions = client.defaultQueryOptions(options)
;(client.getDefaultOptions().queries as any)?._experimental_beforeQuery?.(
defaultedOptions,
)
if (process.env.NODE_ENV !== 'production') {
if (!defaultedOptions.queryFn) {
console.error(
`[${defaultedOptions.queryHash}]: No queryFn was passed as an option, and no default queryFn was found. The queryFn parameter is only optional when using a default queryFn. More info here: https://tanstack.com/query/latest/docs/framework/react/guides/default-query-function`,
)
}
}
// Make sure results are optimistically set in fetching state before subscribing or updating options
defaultedOptions._optimisticResults = isRestoring
? 'isRestoring'
: 'optimistic'
ensureSuspenseTimers(defaultedOptions)
ensurePreventErrorBoundaryRetry(defaultedOptions, errorResetBoundary)
useClearResetErrorBoundary(errorResetBoundary)
// this needs to be invoked before creating the Observer because that can create a cache entry
const isNewCacheEntry = !client
.getQueryCache()
.get(defaultedOptions.queryHash)
const [observer] = React.useState(
() =>
new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(
client,
defaultedOptions,
),
)
// note: this must be called before useSyncExternalStore
const result = observer.getOptimisticResult(defaultedOptions)
const shouldSubscribe = !isRestoring && options.subscribed !== false
React.useSyncExternalStore(
React.useCallback(
(onStoreChange) => {
const unsubscribe = shouldSubscribe
? observer.subscribe(notifyManager.batchCalls(onStoreChange))
: noop
// Update result to make sure we did not miss any query updates
// between creating the observer and subscribing to it.
observer.updateResult()
return unsubscribe
},
[observer, shouldSubscribe],
),
() => observer.getCurrentResult(),
() => observer.getCurrentResult(),
)
React.useEffect(() => {
observer.setOptions(defaultedOptions)
}, [defaultedOptions, observer])
// Handle suspense
if (shouldSuspend(defaultedOptions, result)) {
throw fetchOptimistic(defaultedOptions, observer, errorResetBoundary)
}
// Handle error boundary
if (
getHasError({
result,
errorResetBoundary,
throwOnError: defaultedOptions.throwOnError,
query: client
.getQueryCache()
.get<
TQueryFnData,
TError,
TQueryData,
TQueryKey
>(defaultedOptions.queryHash),
suspense: defaultedOptions.suspense,
})
) {
throw result.error
}
;(client.getDefaultOptions().queries as any)?._experimental_afterQuery?.(
defaultedOptions,
result,
)
if (
defaultedOptions.experimental_prefetchInRender &&
!isServer &&
willFetch(result, isRestoring)
) {
const promise = isNewCacheEntry
? // Fetch immediately on render in order to ensure `.promise` is resolved even if the component is unmounted
fetchOptimistic(defaultedOptions, observer, errorResetBoundary)
: // subscribe to the "cache promise" so that we can finalize the currentThenable once data comes in
client.getQueryCache().get(defaultedOptions.queryHash)?.promise
promise?.catch(noop).finally(() => {
// `.updateResult()` will trigger `.#currentThenable` to finalize
observer.updateResult()
})
}
// Handle result property usage tracking
return !defaultedOptions.notifyOnChangeProps
? observer.trackResult(result)
: result
}
|