diff --git a/__tests__/atomWithQuery_spec.tsx b/__tests__/atomWithQuery_spec.tsx index f337963..86de580 100644 --- a/__tests__/atomWithQuery_spec.tsx +++ b/__tests__/atomWithQuery_spec.tsx @@ -390,7 +390,10 @@ it('query with enabled (#500)', async () => { }) it('query with initialData test', async () => { - const mockFetch = jest.fn((response) => ({ response })) + const mockFetch = jest.fn((response: { count: number }) => ({ + response, + })) + let resolve = () => {} const countAtom = atomWithQuery(() => ({ @@ -402,18 +405,14 @@ it('query with initialData test', async () => { initialData: { response: { count: 0 } }, })) const Counter = () => { - const [countData] = useAtom(countAtom) - const { data, isPending, isError } = countData - - if (isPending) { - return <>loading - } - - if (isError) { - return <>errorred - } + const [ + { + data: { + response: { count }, + }, + }, + ] = useAtom(countAtom) - const count = data.response.count return ( <>
count: {count}
diff --git a/__tests__/atomWithSuspenseQuery_spec.tsx b/__tests__/atomWithSuspenseQuery_spec.tsx index f7bcfac..21db26b 100644 --- a/__tests__/atomWithSuspenseQuery_spec.tsx +++ b/__tests__/atomWithSuspenseQuery_spec.tsx @@ -18,7 +18,7 @@ afterEach(() => { it('suspense basic, suspends', async () => { let resolve = () => {} const countAtom = atomWithSuspenseQuery(() => ({ - queryKey: ['test1'], + queryKey: ['test1', 'suspends'], queryFn: async () => { await new Promise((r) => (resolve = r)) return { response: { count: 0 } } diff --git a/src/atomWithInfiniteQuery.ts b/src/atomWithInfiniteQuery.ts index d3189bf..daffd4a 100644 --- a/src/atomWithInfiniteQuery.ts +++ b/src/atomWithInfiniteQuery.ts @@ -1,16 +1,21 @@ -import { InfiniteQueryObserver, QueryClient } from '@tanstack/query-core' -import type { +import { DefaultError, - DefaultedInfiniteQueryObserverOptions, InfiniteData, - InfiniteQueryObserverOptions, - InfiniteQueryObserverResult, + InfiniteQueryObserver, + QueryClient, QueryKey, - WithRequired, + QueryObserver, } from '@tanstack/query-core' -import { Atom, type Getter, atom } from 'jotai/vanilla' +import { Getter } from 'jotai' import { baseAtomWithQuery } from './baseAtomWithQuery' import { queryClientAtom } from './queryClientAtom' +import { + AtomWithInfiniteQueryOptions, + AtomWithInfiniteQueryResult, + DefinedAtomWithInfiniteQueryResult, + DefinedInitialDataInfiniteOptions, + UndefinedInitialDataInfiniteOptions, +} from './types' export function atomWithInfiniteQuery< TQueryFnData, @@ -21,102 +26,59 @@ export function atomWithInfiniteQuery< >( getOptions: ( get: Getter - ) => InfiniteQueryOptions, - getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) -): Atom> { - const observerCacheAtom = atom( - () => - new WeakMap< - QueryClient, - InfiniteQueryObserver< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - > - >() - ) - if (process.env.NODE_ENV !== 'production') { - observerCacheAtom.debugPrivate = true - } - const optionsAtom = atom((get) => { - const client = getQueryClient(get) - const options = getOptions(get) - const cache = get(observerCacheAtom) - const cachedObserver = cache.get(client) - const dOptions = client.defaultQueryOptions( - options - ) as DefaultedInfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - > - - dOptions._optimisticResults = 'optimistic' - - if (cachedObserver) { - cachedObserver.setOptions(dOptions, { listeners: false }) - } - - return dOptions - }) - if (process.env.NODE_ENV !== 'production') { - optionsAtom.debugPrivate = true - } - const observerAtom = atom((get) => { - const options = get(optionsAtom) - const client = getQueryClient(get) - - const observerCache = get(observerCacheAtom) - - const cachedObserver = observerCache.get(client) - if (cachedObserver) return cachedObserver - - const newObserver = new InfiniteQueryObserver(client, options) - observerCache.set(client, newObserver) - - return newObserver - }) - - return baseAtomWithQuery< + ) => UndefinedInitialDataInfiniteOptions< TQueryFnData, TError, TData, + TQueryKey, + TPageParam + >, + getQueryClient?: (get: Getter) => QueryClient +): AtomWithInfiniteQueryResult +export function atomWithInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + getOptions: ( + get: Getter + ) => DefinedInitialDataInfiniteOptions< TQueryFnData, + TError, + TData, TQueryKey, TPageParam - >( - (get) => ({ - ...get(optionsAtom), - suspense: false, - }), - (get) => get(observerAtom), - getQueryClient - ) -} - -interface InfiniteQueryOptions< - TQueryFnData = unknown, + >, + getQueryClient?: (get: Getter) => QueryClient +): DefinedAtomWithInfiniteQueryResult +export function atomWithInfiniteQuery< + TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown, -> extends WithRequired< - Omit< - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - >, - 'suspense' - >, - 'queryKey' - > {} +>( + getOptions: ( + get: Getter + ) => AtomWithInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam + >, + getQueryClient?: (get: Getter) => QueryClient +): AtomWithInfiniteQueryResult +export function atomWithInfiniteQuery( + getOptions: (get: Getter) => AtomWithInfiniteQueryOptions, + getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) +) { + return baseAtomWithQuery( + getOptions, + InfiniteQueryObserver as typeof QueryObserver, + getQueryClient + ) +} diff --git a/src/atomWithMutation.ts b/src/atomWithMutation.ts index c15e85c..5f052f1 100644 --- a/src/atomWithMutation.ts +++ b/src/atomWithMutation.ts @@ -1,12 +1,13 @@ import { MutationObserver, - type MutationObserverOptions, type MutationObserverResult, + MutationOptions, QueryClient, } from '@tanstack/query-core' import { Getter, atom } from 'jotai' import { make, pipe, toObservable } from 'wonka' import { queryClientAtom } from './queryClientAtom' +import { AtomWithMutationResult, MutateFunction } from './types' import { shouldThrowError } from './utils' export function atomWithMutation< @@ -17,9 +18,9 @@ export function atomWithMutation< >( getOptions: ( get: Getter - ) => MutationObserverOptions, + ) => MutationOptions, getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) -) { +): AtomWithMutationResult { const IN_RENDER = Symbol() const optionsAtom = atom((get) => { @@ -113,9 +114,9 @@ export function atomWithMutation< const mutateAtom = atom((get) => { const observer = get(observerAtom) - const mutate = ( - variables: TVariables, - options?: MutationObserverOptions + const mutate: MutateFunction = ( + variables, + options ) => { observer.mutate(variables, options).catch(noop) } diff --git a/src/atomWithQuery.ts b/src/atomWithQuery.ts index c74251a..ff5edef 100644 --- a/src/atomWithQuery.ts +++ b/src/atomWithQuery.ts @@ -3,80 +3,54 @@ import { QueryClient, QueryKey, QueryObserver, - QueryObserverOptions, - QueryObserverResult, } from '@tanstack/query-core' -import { Atom, Getter, atom } from 'jotai' +import { Getter } from 'jotai' import { baseAtomWithQuery } from './baseAtomWithQuery' import { queryClientAtom } from './queryClientAtom' +import { + AtomWithQueryOptions, + AtomWithQueryResult, + DefinedAtomWithQueryResult, + DefinedInitialDataOptions, + UndefinedInitialDataOptions, +} from './types' export function atomWithQuery< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, - TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( getOptions: ( get: Getter - ) => Omit< - QueryObserverOptions, - 'suspense' - >, + ) => UndefinedInitialDataOptions, + getQueryClient?: (get: Getter) => QueryClient +): AtomWithQueryResult +export function atomWithQuery< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + getOptions: ( + get: Getter + ) => DefinedInitialDataOptions, + getQueryClient?: (get: Getter) => QueryClient +): DefinedAtomWithQueryResult +export function atomWithQuery< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + getOptions: ( + get: Getter + ) => AtomWithQueryOptions, + getQueryClient?: (get: Getter) => QueryClient +): AtomWithQueryResult +export function atomWithQuery( + getOptions: (get: Getter) => AtomWithQueryOptions, getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) -): Atom> { - const observerCacheAtom = atom( - () => - new WeakMap< - QueryClient, - QueryObserver - >() - ) - if (process.env.NODE_ENV !== 'production') { - observerCacheAtom.debugPrivate = true - } - - const optionsAtom = atom((get) => { - const client = getQueryClient(get) - const options = getOptions(get) - const cache = get(observerCacheAtom) - const cachedObserver = cache.get(client) - const dOptions = client.defaultQueryOptions(options) - - dOptions._optimisticResults = 'optimistic' - - if (cachedObserver) { - cachedObserver.setOptions(dOptions, { listeners: false }) - } - - return dOptions - }) - if (process.env.NODE_ENV !== 'production') { - optionsAtom.debugPrivate = true - } - - const observerAtom = atom((get) => { - const options = get(optionsAtom) - const client = getQueryClient(get) - - const observerCache = get(observerCacheAtom) - - const cachedObserver = observerCache.get(client) - - if (cachedObserver) return cachedObserver - - const newObserver = new QueryObserver(client, options) - observerCache.set(client, newObserver) - - return newObserver - }) - if (process.env.NODE_ENV !== 'production') { - observerAtom.debugPrivate = true - } - - return baseAtomWithQuery( - (get) => ({ ...get(optionsAtom), suspense: false }), - (get) => get(observerAtom), - getQueryClient - ) +) { + return baseAtomWithQuery(getOptions, QueryObserver, getQueryClient) } diff --git a/src/atomWithSuspenseInfiniteQuery.ts b/src/atomWithSuspenseInfiniteQuery.ts index 3033b9d..68a24da 100644 --- a/src/atomWithSuspenseInfiniteQuery.ts +++ b/src/atomWithSuspenseInfiniteQuery.ts @@ -1,19 +1,22 @@ import { DefaultError, - DefaultedInfiniteQueryObserverOptions, InfiniteData, InfiniteQueryObserver, - InfiniteQueryObserverOptions, - InfiniteQueryObserverSuccessResult, QueryClient, - type QueryKey, + QueryKey, + QueryObserver, } from '@tanstack/query-core' -import { Atom, Getter, atom } from 'jotai' +import { Getter, atom } from 'jotai' import { baseAtomWithQuery } from './baseAtomWithQuery' import { queryClientAtom } from './queryClientAtom' +import { + AtomWithSuspenseInfiniteQueryOptions, + AtomWithSuspenseInfiniteQueryResult, +} from './types' +import { defaultThrowOnError } from './utils' -export const atomWithSuspenseInfiniteQuery = < - TQueryFnData = unknown, +export function atomWithSuspenseInfiniteQuery< + TQueryFnData, TError = DefaultError, TData = InfiniteData, TQueryKey extends QueryKey = QueryKey, @@ -21,116 +24,29 @@ export const atomWithSuspenseInfiniteQuery = < >( getOptions: ( get: Getter - ) => SuspenseInfiniteQueryOptions< + ) => AtomWithSuspenseInfiniteQueryOptions< TQueryFnData, TError, - TPageParam, TData, - TQueryKey + TQueryFnData, + TQueryKey, + TPageParam >, getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) -): Atom>> => { - const observerCacheAtom = atom( - () => - new WeakMap< - QueryClient, - InfiniteQueryObserver< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - > - >() - ) - if (process.env.NODE_ENV !== 'production') { - observerCacheAtom.debugPrivate = true - } - - const optionsAtom = atom((get) => { - const client = getQueryClient(get) +): AtomWithSuspenseInfiniteQueryResult { + const suspenseOptionsAtom = atom((get) => { const options = getOptions(get) - const cache = get(observerCacheAtom) - const cachedObserver = cache.get(client) - const dOptions = client.defaultQueryOptions( - options - ) as DefaultedInfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - > - - dOptions._optimisticResults = 'optimistic' - - if (typeof dOptions.staleTime !== 'number') { - dOptions.staleTime = 1000 - } - - if (cachedObserver) { - cachedObserver.setOptions(dOptions, { listeners: false }) + return { + ...options, + enabled: true, + suspense: true, + throwOnError: defaultThrowOnError, } - - return dOptions - }) - if (process.env.NODE_ENV !== 'production') { - optionsAtom.debugPrivate = true - } - - const observerAtom = atom((get) => { - const options = get(optionsAtom) - const client = getQueryClient(get) - - const observerCache = get(observerCacheAtom) - - const cachedObserver = observerCache.get(client) - - if (cachedObserver) return cachedObserver - - const newObserver = new InfiniteQueryObserver(client, options) - observerCache.set(client, newObserver) - - return newObserver }) - if (process.env.NODE_ENV !== 'production') { - observerAtom.debugPrivate = true - } - return baseAtomWithQuery< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - >( - (get) => ({ - ...get(optionsAtom), - suspense: true, - enabled: true, - }), - (get) => get(observerAtom), + return baseAtomWithQuery( + (get: Getter) => get(suspenseOptionsAtom), + InfiniteQueryObserver as typeof QueryObserver, getQueryClient - ) + ) as unknown as AtomWithSuspenseInfiniteQueryResult } - -interface SuspenseInfiniteQueryOptions< - TQueryFnData = unknown, - TError = DefaultError, - TPageParam = unknown, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, -> extends Omit< - InfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - >, - 'enabled' | 'throwOnError' | 'placeholderData' - > {} diff --git a/src/atomWithSuspenseQuery.ts b/src/atomWithSuspenseQuery.ts index b20e65a..e9e0763 100644 --- a/src/atomWithSuspenseQuery.ts +++ b/src/atomWithSuspenseQuery.ts @@ -1,93 +1,42 @@ import { DefaultError, - type DefinedQueryObserverResult, QueryClient, - type QueryKey, + QueryKey, QueryObserver, - type QueryObserverOptions, } from '@tanstack/query-core' -import { Atom, Getter, atom } from 'jotai' +import { Getter, atom } from 'jotai' import { baseAtomWithQuery } from './baseAtomWithQuery' import { queryClientAtom } from './queryClientAtom' +import { + AtomWithSuspenseQueryOptions, + AtomWithSuspenseQueryResult, +} from './types' +import { defaultThrowOnError } from './utils' -export const atomWithSuspenseQuery = < +export function atomWithSuspenseQuery< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, - TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( getOptions: ( get: Getter - ) => Omit< - QueryObserverOptions, - 'suspense' | 'enabled' - >, + ) => AtomWithSuspenseQueryOptions, getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) -): Atom< - | DefinedQueryObserverResult - | Promise> -> => { - const observerCacheAtom = atom( - () => - new WeakMap< - QueryClient, - QueryObserver - >() - ) - if (process.env.NODE_ENV !== 'production') { - observerCacheAtom.debugPrivate = true - } - - const optionsAtom = atom((get) => { - const client = getQueryClient(get) +): AtomWithSuspenseQueryResult { + const suspenseOptions = atom((get) => { const options = getOptions(get) - const cache = get(observerCacheAtom) - const cachedObserver = cache.get(client) - const dOptions = client.defaultQueryOptions(options) - - dOptions._optimisticResults = 'optimistic' - - if (typeof dOptions.staleTime !== 'number') { - dOptions.staleTime = 1000 - } - - if (cachedObserver) { - cachedObserver.setOptions(dOptions, { listeners: false }) + return { + ...options, + suspense: true, + enabled: true, + throwOnError: defaultThrowOnError, } - - return dOptions }) - if (process.env.NODE_ENV !== 'production') { - optionsAtom.debugPrivate = true - } - const observerAtom = atom((get) => { - const options = get(optionsAtom) - const client = getQueryClient(get) - - const observerCache = get(observerCacheAtom) - - const cachedObserver = observerCache.get(client) - - if (cachedObserver) return cachedObserver - - const newObserver = new QueryObserver(client, options) - observerCache.set(client, newObserver) - - return newObserver - }) - if (process.env.NODE_ENV !== 'production') { - observerAtom.debugPrivate = true - } - - return baseAtomWithQuery( - (get) => ({ - ...get(optionsAtom), - suspense: true, - enabled: true, - }), - (get) => get(observerAtom), + return baseAtomWithQuery( + (get: Getter) => get(suspenseOptions), + QueryObserver, getQueryClient - ) + ) as AtomWithSuspenseQueryResult } diff --git a/src/baseAtomWithQuery.ts b/src/baseAtomWithQuery.ts index ba2419a..112ba9b 100644 --- a/src/baseAtomWithQuery.ts +++ b/src/baseAtomWithQuery.ts @@ -1,179 +1,113 @@ import { - DefaultError, - DefaultedInfiniteQueryObserverOptions, - DefaultedQueryObserverOptions, - InfiniteQueryObserver, - InfiniteQueryObserverResult, - InfiniteQueryObserverSuccessResult, QueryClient, QueryKey, QueryObserver, QueryObserverResult, - QueryObserverSuccessResult, } from '@tanstack/query-core' import { Atom, Getter, atom } from 'jotai' -import { make, pipe, toObservable } from 'wonka' import { queryClientAtom } from './queryClientAtom' -import { getHasError, shouldSuspend } from './utils' +import { BaseAtomWithQueryOptions } from './types' +import { ensureStaleTime, getHasError, shouldSuspend } from './utils' export function baseAtomWithQuery< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey extends QueryKey, >( getOptions: ( get: Getter - ) => DefaultedQueryObserverOptions< + ) => BaseAtomWithQueryOptions< TQueryFnData, TError, TData, TQueryData, TQueryKey - > & { suspense: true; enabled: true }, - getObserver: ( - get: Getter - ) => QueryObserver, - getQueryClient?: (get: Getter) => QueryClient -): Atom>> -export function baseAtomWithQuery< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - getOptions: ( - get: Getter - ) => DefaultedQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > & { suspense: false }, - getObserver: ( - get: Getter - ) => QueryObserver, - getQueryClient?: (get: Getter) => QueryClient -): Atom> -export function baseAtomWithQuery< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, ->( - getOptions: ( - get: Getter - ) => DefaultedInfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey, - TPageParam - > & { suspense: true; enabled: true }, - getObserver: ( - get: Getter - ) => InfiniteQueryObserver< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam >, - getQueryClient?: (get: Getter) => QueryClient -): Atom>> -export function baseAtomWithQuery< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, ->( - getOptions: ( - get: Getter - ) => DefaultedInfiniteQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey, - TPageParam - > & { suspense: false }, - getObserver: ( - get: Getter - ) => InfiniteQueryObserver< - TQueryFnData, - TError, - TData, - TQueryFnData, - TQueryKey, - TPageParam - >, - getQueryClient?: (get: Getter) => QueryClient -): Atom> -export function baseAtomWithQuery< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, ->( - getOptions: ( - get: Getter - ) => DefaultedQueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >, - getObserver: ( - get: Getter - ) => QueryObserver, + Observer: typeof QueryObserver, getQueryClient: (get: Getter) => QueryClient = (get) => get(queryClientAtom) -) { +): Atom< + | QueryObserverResult + | Promise> +> { const resetAtom = atom(0) if (process.env.NODE_ENV !== 'production') { resetAtom.debugPrivate = true } - const observableAtom = atom((get) => { - const observer = getObserver(get) - const source = make>(({ next }) => { - const callback = (result: QueryObserverResult) => { - next(result) - } + const clientAtom = atom(getQueryClient) + if (process.env.NODE_ENV !== 'production') { + clientAtom.debugPrivate = true + } + + const observerCacheAtom = atom( + () => + new WeakMap< + QueryClient, + QueryObserver + >() + ) + if (process.env.NODE_ENV !== 'production') { + observerCacheAtom.debugPrivate = true + } - return observer.subscribe(callback) - }) - return pipe(source, toObservable) + 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') { - observableAtom.debugPrivate = true + observerAtom.debugPrivate = true } const dataAtom = atom((get) => { - const observer = getObserver(get) - const observable = get(observableAtom) + const observer = get(observerAtom) const currentResult = observer.getCurrentResult() + const resultAtom = atom(currentResult) if (process.env.NODE_ENV !== 'production') { resultAtom.debugPrivate = true } resultAtom.onMount = (set) => { - const { unsubscribe } = observable.subscribe((state) => { + const unsubscribe = observer.subscribe((state) => { set(state) }) - return () => unsubscribe() + return unsubscribe } return resultAtom @@ -183,8 +117,8 @@ export function baseAtomWithQuery< } return atom((get) => { - const observer = getObserver(get) - const options = getOptions(get) + const observer = get(observerAtom) + const defaultedOptions = get(defaultedOptionsAtom) const client = getQueryClient(get) @@ -199,17 +133,17 @@ export function baseAtomWithQuery< get(resetAtom) get(get(dataAtom)) - const result = observer.getOptimisticResult(options) + const result = observer.getOptimisticResult(defaultedOptions) - if (shouldSuspend(options, result, false)) { - return observer.fetchOptimistic(options) + if (shouldSuspend(defaultedOptions, result, false)) { + return observer.fetchOptimistic(defaultedOptions) } if ( getHasError({ result, query: observer.getCurrentQuery(), - throwOnError: options.throwOnError, + throwOnError: defaultedOptions.throwOnError, }) ) { throw result.error diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..c0c6be4 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,230 @@ +import { + DefaultError, + DefinedInfiniteQueryObserverResult, + DefinedQueryObserverResult, + InfiniteData, + InfiniteQueryObserverOptions, + InfiniteQueryObserverResult, + MutationObserverOptions, + MutationObserverResult, + QueryKey, + MutateFunction as QueryMutateFunction, + QueryObserverOptions, + QueryObserverResult, + WithRequired, +} from '@tanstack/query-core' +import { Atom } from 'jotai' + +type Override = { [K in keyof A]: K extends keyof B ? B[K] : A[K] } + +export type MutateFunction< + TData = unknown, + TError = DefaultError, + TVariables = void, + TContext = unknown, +> = ( + ...args: Parameters> +) => void + +export type MutateAsyncFunction< + TData = unknown, + TError = DefaultError, + TVariables = void, + TContext = unknown, +> = QueryMutateFunction + +export type AtomWithMutationResult = Atom< + Override< + MutationObserverResult, + { mutate: MutateFunction } + > & { + mutateAsync: MutateAsyncFunction + } +> + +export interface MutationOptions< + TData = unknown, + TError = DefaultError, + TVariables = void, + TContext = unknown, +> extends Omit< + MutationObserverOptions, + '_defaulted' | 'variables' + > {} + +export interface BaseAtomWithQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends WithRequired< + QueryObserverOptions, + 'queryKey' + > {} + +export interface AtomWithQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends Omit< + WithRequired< + BaseAtomWithQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, + 'queryKey' + >, + 'suspense' + > {} + +export interface AtomWithSuspenseQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends Omit< + AtomWithQueryOptions, + 'enabled' | 'throwOnError' | 'placeholderData' + > {} + +export interface AtomWithInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> extends WithRequired< + Omit< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + >, + 'suspense' + >, + 'queryKey' + > {} + +export interface AtomWithSuspenseInfiniteQueryOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> extends Omit< + AtomWithInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey, + TPageParam + >, + 'enabled' | 'throwOnError' | 'placeholderData' + > {} + +export type AtomWithQueryResult = Atom< + QueryObserverResult +> + +export type DefinedAtomWithQueryResult< + TData = unknown, + TError = DefaultError, +> = Atom> + +export type AtomWithSuspenseQueryResult< + TData = unknown, + TError = DefaultError, +> = Atom< + | Omit, 'isPlaceholderData'> + | Promise< + Omit, 'isPlaceholderData'> + > +> + +export type AtomWithInfiniteQueryResult< + TData = unknown, + TError = DefaultError, +> = Atom> + +export type DefinedAtomWithInfiniteQueryResult< + TData = unknown, + TError = DefaultError, +> = Atom> + +export type AtomWithSuspenseInfiniteQueryResult< + TData = unknown, + TError = DefaultError, +> = Atom< + Promise< + Omit, 'isPlaceholderData'> + > +> + +export type UndefinedInitialDataOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = AtomWithQueryOptions & { + initialData?: undefined +} + +type NonUndefinedGuard = T extends undefined ? never : T + +export type DefinedInitialDataOptions< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = AtomWithQueryOptions & { + initialData: + | NonUndefinedGuard + | (() => NonUndefinedGuard) +} + +export type UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = AtomWithInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam +> & { + initialData?: undefined +} + +export type DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +> = AtomWithInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey, + TPageParam +> & { + initialData: + | NonUndefinedGuard> + | (() => NonUndefinedGuard>) +} diff --git a/src/utils.ts b/src/utils.ts index 49bf13b..a42a721 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ import type { + DefaultError, DefaultedQueryObserverOptions, Query, QueryKey, @@ -54,3 +55,28 @@ export function shouldThrowError boolean>( return !!throwOnError } + +export const defaultThrowOnError = < + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +>( + _error: TError, + query: Query +) => typeof query.state.data === 'undefined' + +export const ensureStaleTime = ( + defaultedOptions: DefaultedQueryObserverOptions +) => { + if (defaultedOptions.suspense) { + if (typeof defaultedOptions.staleTime !== 'number') { + return { + ...defaultedOptions, + staleTime: 1000, + } + } + } + + return defaultedOptions +}