UNPKG

@tanstack/vue-query

Version:

Hooks for managing, caching and syncing asynchronous and remote data in Vue

222 lines (197 loc) 5.45 kB
import { computed, getCurrentScope, onScopeDispose, reactive, readonly, shallowReactive, shallowReadonly, toRefs, watch, } from 'vue-demi' import { shouldThrowError } from '@tanstack/query-core' import { useQueryClient } from './useQueryClient' import { cloneDeepUnref, updateState } from './utils' import type { Ref } from 'vue-demi' import type { DefaultedQueryObserverOptions, QueryKey, QueryObserver, QueryObserverResult, } from '@tanstack/query-core' import type { QueryClient } from './queryClient' import type { UseQueryOptions } from './useQuery' import type { UseInfiniteQueryOptions } from './useInfiniteQuery' export type UseBaseQueryReturnType< TData, TError, TResult = QueryObserverResult<TData, TError>, > = { [K in keyof TResult]: K extends | 'fetchNextPage' | 'fetchPreviousPage' | 'refetch' ? TResult[K] : Ref<Readonly<TResult>[K]> } & { suspense: () => Promise<TResult> } type UseQueryOptionsGeneric< TQueryFnData, TError, TData, TQueryData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, > = | UseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> | UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> export function useBaseQuery< TQueryFnData, TError, TData, TQueryData, TQueryKey extends QueryKey, TPageParam, >( Observer: typeof QueryObserver, options: UseQueryOptionsGeneric< TQueryFnData, TError, TData, TQueryData, TQueryKey, TPageParam >, queryClient?: QueryClient, ): UseBaseQueryReturnType<TData, TError> { if (process.env.NODE_ENV === 'development') { if (!getCurrentScope()) { console.warn( 'vue-query composable like "useQuery()" should only be used inside a "setup()" function or a running effect scope. They might otherwise lead to memory leaks.', ) } } const client = queryClient || useQueryClient() const defaultedOptions = computed(() => { const clonedOptions = cloneDeepUnref(options as any) if (typeof clonedOptions.enabled === 'function') { clonedOptions.enabled = clonedOptions.enabled() } const defaulted: DefaultedQueryObserverOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey > = client.defaultQueryOptions(clonedOptions) defaulted._optimisticResults = client.isRestoring?.value ? 'isRestoring' : 'optimistic' return defaulted }) const observer = new Observer(client, defaultedOptions.value) // @ts-expect-error const state = defaultedOptions.value.shallow ? shallowReactive(observer.getCurrentResult()) : reactive(observer.getCurrentResult()) let unsubscribe = () => { // noop } if (client.isRestoring) { watch( client.isRestoring, (isRestoring) => { if (!isRestoring) { unsubscribe() unsubscribe = observer.subscribe((result) => { updateState(state, result) }) } }, { immediate: true }, ) } const updater = () => { observer.setOptions(defaultedOptions.value) updateState(state, observer.getCurrentResult()) } watch(defaultedOptions, updater) onScopeDispose(() => { unsubscribe() }) // fix #5910 const refetch = (...args: Parameters<(typeof state)['refetch']>) => { updater() return state.refetch(...args) } const suspense = () => { return new Promise<QueryObserverResult<TData, TError>>( (resolve, reject) => { let stopWatch = () => { // noop } const run = () => { if (defaultedOptions.value.enabled !== false) { // fix #6133 observer.setOptions(defaultedOptions.value) const optimisticResult = observer.getOptimisticResult( defaultedOptions.value, ) if (optimisticResult.isStale) { stopWatch() observer .fetchOptimistic(defaultedOptions.value) .then(resolve, (error: TError) => { if ( shouldThrowError(defaultedOptions.value.throwOnError, [ error, observer.getCurrentQuery(), ]) ) { reject(error) } else { resolve(observer.getCurrentResult()) } }) } else { stopWatch() resolve(optimisticResult) } } } run() stopWatch = watch(defaultedOptions, run) }, ) } // Handle error boundary watch( () => state.error, (error) => { if ( state.isError && !state.isFetching && shouldThrowError(defaultedOptions.value.throwOnError, [ error as TError, observer.getCurrentQuery(), ]) ) { throw error } }, ) // @ts-expect-error const readonlyState = defaultedOptions.value.shallow ? shallowReadonly(state) : readonly(state) const object: any = toRefs(readonlyState) for (const key in state) { if (typeof state[key as keyof typeof state] === 'function') { object[key] = state[key as keyof typeof state] } } object.suspense = suspense object.refetch = refetch return object as UseBaseQueryReturnType<TData, TError> }