Skip to content

feat(react-query): backport v5 apis about infinite query #9334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: v4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions packages/query-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -568,11 +568,17 @@ export interface InfiniteQueryObserverSuccessResult<
status: 'success'
}

export type DefinedInfiniteQueryObserverResult<
TData = unknown,
TError = unknown,
> =
| InfiniteQueryObserverRefetchErrorResult<TData, TError>
| InfiniteQueryObserverSuccessResult<TData, TError>

export type InfiniteQueryObserverResult<TData = unknown, TError = unknown> =
| DefinedInfiniteQueryObserverResult<TData, TError>
| InfiniteQueryObserverLoadingErrorResult<TData, TError>
| InfiniteQueryObserverLoadingResult<TData, TError>
| InfiniteQueryObserverRefetchErrorResult<TData, TError>
| InfiniteQueryObserverSuccessResult<TData, TError>

export type MutationKey = readonly unknown[]

Expand Down
13 changes: 13 additions & 0 deletions packages/react-query/src/__tests__/infiniteQueryOptions.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { infiniteQueryOptions } from '../infiniteQueryOptions'

describe('infiniteQueryOptions', () => {
it('should return the object received as a parameter without any modification.', () => {
const object = {
queryKey: ['key'],
queryFn: () => Promise.resolve(5),
getNextPageParam: () => null,
} as const

expect(infiniteQueryOptions(object)).toStrictEqual(object)
})
})
111 changes: 111 additions & 0 deletions packages/react-query/src/__tests__/infiniteQueryOptions.types.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { expectTypeOf } from 'expect-type'
import {
type InfiniteData,
type UseInfiniteQueryResult,
useInfiniteQuery,
useQueryClient,
} from '@tanstack/react-query'

import { useSuspenseInfiniteQuery } from '../useSuspenseInfiniteQuery'
import { infiniteQueryOptions } from '../infiniteQueryOptions'
import { doNotExecute } from './utils'
import type {
DefinedUseInfiniteQueryResult,
UseSuspenseInfiniteQueryResult,
} from '../types'

const infiniteQuery = {
options: () =>
infiniteQueryOptions({
queryKey: ['key', 1] as const,
queryFn: () => Promise.resolve({ field: 'success' }),
}),
optionsWithInitialData: () =>
infiniteQueryOptions({
queryKey: ['key', 2] as const,
queryFn: () => Promise.resolve({ field: 'success' }),
initialData: () => ({ pageParams: [], pages: [{ field: 'success' }] }),
}),
}

describe('infiniteQueryOptions', () => {
it('should be used with useInfiniteQuery', () => {
doNotExecute(() => {
expectTypeOf(useInfiniteQuery(infiniteQuery.options())).toEqualTypeOf<
UseInfiniteQueryResult<{ field: string }>
>()

expectTypeOf(
useInfiniteQuery({
...infiniteQuery.options(),
select: (data) => ({
pages: data.pages.map(({ field }) => field),
pageParams: data.pageParams,
}),
}),
).toEqualTypeOf<UseInfiniteQueryResult<string>>()

expectTypeOf(
useInfiniteQuery(infiniteQuery.optionsWithInitialData()),
).toEqualTypeOf<DefinedUseInfiniteQueryResult<{ field: string }>>()

expectTypeOf(
useInfiniteQuery({
...infiniteQuery.optionsWithInitialData(),
select: (data) => ({
pages: data.pages.map(({ field }) => field),
pageParams: data.pageParams,
}),
}),
).toEqualTypeOf<DefinedUseInfiniteQueryResult<string>>()

expectTypeOf(
useInfiniteQuery({
queryKey: ['key', 2] as const,
queryFn: () => Promise.resolve({ field: 'success' }),
initialData: () => ({
pages: [{ field: 'success' }],
pageParams: [],
}),
select: (data) => ({
pages: data.pages.map(({ field }) => field),
pageParams: data.pageParams,
}),
}),
).toEqualTypeOf<DefinedUseInfiniteQueryResult<string>>()
})
})
it('should be used with useSuspenseInfiniteQuery', () => {
doNotExecute(() => {
expectTypeOf(
useSuspenseInfiniteQuery(infiniteQuery.options()),
).toEqualTypeOf<UseSuspenseInfiniteQueryResult<{ field: string }>>()

expectTypeOf(
useSuspenseInfiniteQuery({
...infiniteQuery.options(),
select: (data) => ({
pages: data.pages.map(({ field }) => field),
pageParams: data.pageParams,
}),
}),
).toEqualTypeOf<UseSuspenseInfiniteQueryResult<string>>()
})
})
it('should be used with useQueryClient', () => {
doNotExecute(async () => {
const queryClient = useQueryClient()

queryClient.invalidateQueries(infiniteQuery.options())
queryClient.resetQueries(infiniteQuery.options())
queryClient.removeQueries(infiniteQuery.options())
queryClient.cancelQueries(infiniteQuery.options())
queryClient.prefetchQuery(infiniteQuery.options())
queryClient.refetchQueries(infiniteQuery.options())

expectTypeOf(
await queryClient.fetchQuery(infiniteQuery.options()),
).toEqualTypeOf<InfiniteData<{ field: string }>>()
})
})
})
15 changes: 8 additions & 7 deletions packages/react-query/src/__tests__/queryOptions.types.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ const queryFn = () => Promise.resolve({ field: 'success' })
describe('queryOptions', () => {
it('should be used with useQuery', () => {
doNotExecute(() => {
const dd = useQuery(
queryOptions({
queryKey,
queryFn,
}),
)
expectTypeOf(dd).toEqualTypeOf<UseQueryResult<{ field: string }>>()
expectTypeOf(
useQuery(
queryOptions({
queryKey,
queryFn,
}),
),
).toEqualTypeOf<UseQueryResult<{ field: string }>>()
expectTypeOf(
useQuery({
...queryOptions({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { expectTypeOf } from 'expect-type'
import { infiniteQueryOptions, useSuspenseInfiniteQuery } from '..'
import { doNotExecute, sleep } from './utils'
import type { UseSuspenseInfiniteQueryResult } from '..'

import type { InfiniteData } from '@tanstack/react-query'

const queryKey = ['key'] as const
const queryFn = () => sleep(10).then(() => ({ text: 'response' }))

describe('useSuspenseInfiniteQuery', () => {
it('type check', () => {
doNotExecute(() => {
// @ts-expect-error no arg
useSuspenseInfiniteQuery()

useSuspenseInfiniteQuery({
queryKey,
queryFn,
// @ts-expect-error no suspense
suspense: boolean,
})
useSuspenseInfiniteQuery({
queryKey,
queryFn,
// @ts-expect-error no useErrorBoundary
useErrorBoundary: boolean,
})
useSuspenseInfiniteQuery({
queryKey,
queryFn,
// @ts-expect-error no enabled
enabled: boolean,
})
useSuspenseInfiniteQuery({
queryKey,
queryFn,
// @ts-expect-error no placeholderData
placeholderData: 'placeholder',
})
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
useSuspenseInfiniteQuery({
queryKey,
queryFn,
// @ts-expect-error no isPlaceholderData
}).isPlaceholderData
useSuspenseInfiniteQuery({
queryKey,
queryFn,
//@ts-expect-error no networkMode
networkMode: 'always',
})

const infiniteQuery = useSuspenseInfiniteQuery({ queryKey, queryFn })
expectTypeOf(infiniteQuery).toEqualTypeOf<
UseSuspenseInfiniteQueryResult<{ text: string }>
>()
expectTypeOf(infiniteQuery.data).toEqualTypeOf<
InfiniteData<{ text: string }>
>()
expectTypeOf(infiniteQuery.status).toEqualTypeOf<'error' | 'success'>()

const selectedInfiniteQuery = useSuspenseInfiniteQuery({
queryKey,
queryFn,
select: (data) => ({
pages: data.pages.map(({ text }) => text),
pageParams: data.pageParams,
}),
})
expectTypeOf(selectedInfiniteQuery).toEqualTypeOf<
UseSuspenseInfiniteQueryResult<string>
>()
expectTypeOf(selectedInfiniteQuery.data).toEqualTypeOf<
InfiniteData<string>
>()
expectTypeOf(selectedInfiniteQuery.status).toEqualTypeOf<
'error' | 'success'
>()

const options = infiniteQueryOptions({
queryKey,
queryFn,
})

const infiniteQueryWithOptions = useSuspenseInfiniteQuery(options)
expectTypeOf(infiniteQueryWithOptions).toEqualTypeOf<
UseSuspenseInfiniteQueryResult<{ text: string }>
>()
expectTypeOf(infiniteQueryWithOptions.data).toEqualTypeOf<
InfiniteData<{ text: string }>
>()
expectTypeOf(infiniteQueryWithOptions.status).toEqualTypeOf<
'error' | 'success'
>()

const selectedInfiniteQueryWithOptions = useSuspenseInfiniteQuery({
...options,
select: (data) => ({
pages: data.pages.map(({ text }) => text),
pageParams: data.pageParams,
}),
})
expectTypeOf(selectedInfiniteQueryWithOptions).toEqualTypeOf<
UseSuspenseInfiniteQueryResult<string>
>()
expectTypeOf(selectedInfiniteQueryWithOptions.data).toEqualTypeOf<
InfiniteData<string>
>()
expectTypeOf(selectedInfiniteQueryWithOptions.status).toEqualTypeOf<
'error' | 'success'
>()
})
})
})
6 changes: 6 additions & 0 deletions packages/react-query/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { useQueries } from './useQueries'
export type { QueriesResults, QueriesOptions } from './useQueries'
export { useQuery } from './useQuery'
export { useSuspenseQuery } from './useSuspenseQuery'
export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery'
export { useSuspenseQueries } from './useSuspenseQueries'
export type {
SuspenseQueriesResults,
Expand All @@ -22,6 +23,11 @@ export type {
DefinedInitialDataOptions,
UndefinedInitialDataOptions,
} from './queryOptions'
export { infiniteQueryOptions } from './infiniteQueryOptions'
export type {
DefinedInitialDataInfiniteOptions,
UndefinedInitialDataInfiniteOptions,
} from './infiniteQueryOptions'
export {
defaultContext,
QueryClientProvider,
Expand Down
95 changes: 95 additions & 0 deletions packages/react-query/src/infiniteQueryOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { UseInfiniteQueryOptions } from './types'
import type {
InfiniteData,
NonUndefinedGuard,
OmitKeyof,
QueryKey,
WithRequired,
} from '@tanstack/query-core'

type UseInfiniteQueryOptionsOmitted<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = OmitKeyof<
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
'onSuccess' | 'onError' | 'onSettled' | 'refetchInterval'
>

type ProhibitedInfiniteQueryOptionsKeyInV5 = keyof Pick<
UseInfiniteQueryOptionsOmitted,
'useErrorBoundary' | 'suspense'
>

export type UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = UseInfiniteQueryOptionsOmitted<TQueryFnData, TError, TData, TQueryKey> & {
initialData?: undefined
}

export type DefinedInitialDataInfiniteOptions<
TQueryFnData,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = UseInfiniteQueryOptionsOmitted<TQueryFnData, TError, TData, TQueryKey> & {
initialData:
| NonUndefinedGuard<InfiniteData<TQueryFnData>>
| (() => NonUndefinedGuard<InfiniteData<TQueryFnData>>)
| undefined
}

export function infiniteQueryOptions<
TQueryFnData,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: WithRequired<
OmitKeyof<
DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>,
ProhibitedInfiniteQueryOptionsKeyInV5
>,
'queryKey'
>,
): WithRequired<
OmitKeyof<
DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>,
ProhibitedInfiniteQueryOptionsKeyInV5
>,
'queryKey'
>

export function infiniteQueryOptions<
TQueryFnData,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: WithRequired<
OmitKeyof<
UndefinedInitialDataInfiniteOptions<
TQueryFnData,
TError,
TData,
TQueryKey
>,
ProhibitedInfiniteQueryOptionsKeyInV5
>,
'queryKey'
>,
): WithRequired<
OmitKeyof<
UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey>,
ProhibitedInfiniteQueryOptionsKeyInV5
>,
'queryKey'
>

export function infiniteQueryOptions(options: unknown) {
return options
}
Loading
Loading