jotai-tanstack-query
Version:
150 lines (127 loc) • 3.7 kB
text/typescript
import {
QueryClient,
QueryKey,
QueryObserver,
QueryObserverResult,
notifyManager,
} from '@tanstack/query-core'
import { Getter, WritableAtom, atom } from 'jotai'
import { queryClientAtom } from './queryClientAtom'
import { BaseAtomWithQueryOptions } from './types'
import { ensureStaleTime, getHasError, shouldSuspend } from './utils'
export function baseAtomWithQuery<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey extends QueryKey,
>(
getOptions: (
get: Getter
) => BaseAtomWithQueryOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
>,
Observer: typeof QueryObserver,
getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom)
): WritableAtom<
| QueryObserverResult<TData, TError>
| Promise<QueryObserverResult<TData, TError>>,
[],
void
> {
const refreshAtom = atom(0)
const clientAtom = atom(getQueryClient)
if (process.env.NODE_ENV !== 'production') {
clientAtom.debugPrivate = true
}
const observerCacheAtom = atom(
() =>
new WeakMap<
QueryClient,
QueryObserver<TQueryFnData, TError, TData, TQueryData, TQueryKey>
>()
)
if (process.env.NODE_ENV !== 'production') {
observerCacheAtom.debugPrivate = true
}
const defaultedOptionsAtom = atom((get) => {
const client = get(clientAtom)
const options = getOptions(get)
const defaultedOptions = client.defaultQueryOptions(options)
const cache = get(observerCacheAtom)
const cachedObserver = cache.get(client)
defaultedOptions._optimisticResults = 'optimistic'
if (cachedObserver) {
cachedObserver.setOptions(defaultedOptions, {
listeners: false,
})
}
return ensureStaleTime(defaultedOptions)
})
if (process.env.NODE_ENV !== 'production') {
defaultedOptionsAtom.debugPrivate = true
}
const observerAtom = atom((get) => {
const client = get(clientAtom)
const defaultedOptions = get(defaultedOptionsAtom)
const observerCache = get(observerCacheAtom)
const cachedObserver = observerCache.get(client)
if (cachedObserver) return cachedObserver
const newObserver = new Observer(client, defaultedOptions)
observerCache.set(client, newObserver)
return newObserver
})
if (process.env.NODE_ENV !== 'production') {
observerAtom.debugPrivate = true
}
const dataAtom = atom((get) => {
const observer = get(observerAtom)
const defaultedOptions = get(defaultedOptionsAtom)
const result = observer.getOptimisticResult(defaultedOptions)
const resultAtom = atom(result)
if (process.env.NODE_ENV !== 'production') {
resultAtom.debugPrivate = true
}
resultAtom.onMount = (set) => {
const unsubscribe = observer.subscribe(notifyManager.batchCalls(set))
return () => {
if (observer.getCurrentResult().isError) {
observer.getCurrentQuery().reset()
}
unsubscribe()
}
}
return resultAtom
})
if (process.env.NODE_ENV !== 'production') {
dataAtom.debugPrivate = true
}
return atom(
(get) => {
get(refreshAtom)
const observer = get(observerAtom)
const defaultedOptions = get(defaultedOptionsAtom)
const result = get(get(dataAtom))
if (shouldSuspend(defaultedOptions, result, false)) {
return observer.fetchOptimistic(defaultedOptions)
}
if (
getHasError({
result,
query: observer.getCurrentQuery(),
throwOnError: defaultedOptions.throwOnError,
})
) {
throw result.error
}
return result
},
(_get, set) => {
set(refreshAtom, (c) => c + 1)
}
)
}