Skip to content

Added a proof of concept for initialData making TData never undefined #3834

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

Merged
merged 9 commits into from
Jul 16, 2022
Merged
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
4 changes: 3 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
useQuery,
UseQueryResult,
useQueries,
DefinedUseQueryResult,
} from '@tanstack/react-query'
import {
createQueryClient,
Expand Down Expand Up @@ -200,7 +201,7 @@ describe('PersistQueryClientProvider', () => {

test('should show initialData while restoring', async () => {
const key = queryKey()
const states: UseQueryResult<string>[] = []
const states: DefinedUseQueryResult<string>[] = []

const queryClient = createQueryClient()
await queryClient.prefetchQuery(key, () => Promise.resolve('hydrated'))
Expand Down
25 changes: 13 additions & 12 deletions packages/react-query/src/__tests__/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
QueryFunction,
QueryFunctionContext,
UseQueryOptions,
DefinedUseQueryResult,
} from '..'
import { ErrorBoundary } from 'react-error-boundary'

Expand Down Expand Up @@ -142,7 +143,7 @@ describe('useQuery', () => {
) => Promise<TQueryFnData>,
options?: Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn'
'queryKey' | 'queryFn' | 'initialData'
>,
) => useQuery(qk, () => fetcher(qk[1], 'token'), options)
const test = useWrappedQuery([''], async () => '1')
Expand All @@ -159,7 +160,7 @@ describe('useQuery', () => {
fetcher: () => Promise<TQueryFnData>,
options?: Omit<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn'
'queryKey' | 'queryFn' | 'initialData'
>,
) => useQuery(qk, fetcher, options)
const testFuncStyle = useWrappedFuncStyleQuery([''], async () => true)
Expand Down Expand Up @@ -1295,7 +1296,7 @@ describe('useQuery', () => {

it('should use query function from hook when the existing query does not have a query function', async () => {
const key = queryKey()
const results: UseQueryResult<string>[] = []
const results: DefinedUseQueryResult<string>[] = []

queryClient.setQueryData(key, 'set')

Expand Down Expand Up @@ -1712,7 +1713,7 @@ describe('useQuery', () => {

it('should not show initial data from next query if keepPreviousData is set', async () => {
const key = queryKey()
const states: UseQueryResult<number>[] = []
const states: DefinedUseQueryResult<number>[] = []

function Page() {
const [count, setCount] = React.useState(0)
Expand Down Expand Up @@ -3025,7 +3026,7 @@ describe('useQuery', () => {

it('should fetch if initial data is set', async () => {
const key = queryKey()
const states: UseQueryResult<string>[] = []
const states: DefinedUseQueryResult<string>[] = []

function Page() {
const state = useQuery(key, () => 'data', {
Expand Down Expand Up @@ -3055,7 +3056,7 @@ describe('useQuery', () => {

it('should not fetch if initial data is set with a stale time', async () => {
const key = queryKey()
const states: UseQueryResult<string>[] = []
const states: DefinedUseQueryResult<string>[] = []

function Page() {
const state = useQuery(key, () => 'data', {
Expand Down Expand Up @@ -3085,7 +3086,7 @@ describe('useQuery', () => {

it('should fetch if initial data updated at is older than stale time', async () => {
const key = queryKey()
const states: UseQueryResult<string>[] = []
const states: DefinedUseQueryResult<string>[] = []

const oneSecondAgo = Date.now() - 1000

Expand Down Expand Up @@ -3123,7 +3124,7 @@ describe('useQuery', () => {

it('should fetch if "initial data updated at" is exactly 0', async () => {
const key = queryKey()
const states: UseQueryResult<string>[] = []
const states: DefinedUseQueryResult<string>[] = []

function Page() {
const state = useQuery(key, () => 'data', {
Expand Down Expand Up @@ -3154,7 +3155,7 @@ describe('useQuery', () => {

it('should keep initial data when the query key changes', async () => {
const key = queryKey()
const states: UseQueryResult<{ count: number }>[] = []
const states: DefinedUseQueryResult<{ count: number }>[] = []

function Page() {
const [count, setCount] = React.useState(0)
Expand Down Expand Up @@ -3629,7 +3630,7 @@ describe('useQuery', () => {

it('should mark query as fetching, when using initialData', async () => {
const key = queryKey()
const results: UseQueryResult<string>[] = []
const results: DefinedUseQueryResult<string>[] = []

function Page() {
const result = useQuery(key, () => 'serverData', { initialData: 'data' })
Expand All @@ -3648,7 +3649,7 @@ describe('useQuery', () => {

it('should initialize state properly, when initialData is falsy', async () => {
const key = queryKey()
const results: UseQueryResult<number>[] = []
const results: DefinedUseQueryResult<number>[] = []

function Page() {
const result = useQuery(key, () => 1, { initialData: 0 })
Expand All @@ -3668,7 +3669,7 @@ describe('useQuery', () => {
// // See https://github.com/tannerlinsley/react-query/issues/214
it('data should persist when enabled is changed to false', async () => {
const key = queryKey()
const results: UseQueryResult<string>[] = []
const results: DefinedUseQueryResult<string>[] = []

function Page() {
const [shouldFetch, setShouldFetch] = React.useState(true)
Expand Down
157 changes: 157 additions & 0 deletions packages/react-query/src/__tests__/useQuery.types.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { useQuery } from '../useQuery'

export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T,
>() => T extends Y ? 1 : 2
? true
: false

export type Expect<T extends true> = T

const doNotExecute = (_func: () => void) => true

describe('initialData', () => {
describe('Config object overload', () => {
it('TData should always be defined when initialData is provided as an object', () => {
doNotExecute(() => {
const { data } = useQuery({
queryFn: () => {
return {
wow: true,
}
},
initialData: {
wow: true,
},
})

const result: Expect<Equal<{ wow: boolean }, typeof data>> = true
return result
})
})

it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => {
doNotExecute(() => {
const { data } = useQuery({
queryFn: () => {
return {
wow: true,
}
},
initialData: () => ({
wow: true,
}),
})

const result: Expect<Equal<{ wow: boolean }, typeof data>> = true
return result
})
})

it('TData should have undefined in the union when initialData is NOT provided', () => {
doNotExecute(() => {
const { data } = useQuery({
queryFn: () => {
return {
wow: true,
}
},
})

const result: Expect<Equal<{ wow: boolean } | undefined, typeof data>> =
true
return result
})
})

it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => {
doNotExecute(() => {
const { data } = useQuery({
queryFn: () => {
return {
wow: true,
}
},
initialData: () => undefined as { wow: boolean } | undefined,
})

const result: Expect<Equal<{ wow: boolean } | undefined, typeof data>> =
true
return result
})
})
})

describe('Query key overload', () => {
it('TData should always be defined when initialData is provided', () => {
doNotExecute(() => {
const { data } = useQuery(['key'], {
queryFn: () => {
return {
wow: true,
}
},
initialData: {
wow: true,
},
})

const result: Expect<Equal<{ wow: boolean }, typeof data>> = true
return result
})
})

it('TData should have undefined in the union when initialData is NOT provided', () => {
doNotExecute(() => {
const { data } = useQuery(['key'], {
queryFn: () => {
return {
wow: true,
}
},
})

const result: Expect<Equal<{ wow: boolean } | undefined, typeof data>> =
true
return result
})
})
})

describe('Query key and func', () => {
it('TData should always be defined when initialData is provided', () => {
doNotExecute(() => {
const { data } = useQuery(
['key'],
() => {
return {
wow: true,
}
},
{
initialData: {
wow: true,
},
},
)

const result: Expect<Equal<{ wow: boolean }, typeof data>> = true
return result
})
})

it('TData should have undefined in the union when initialData is NOT provided', () => {
doNotExecute(() => {
const { data } = useQuery(['key'], () => {
return {
wow: true,
}
})

const result: Expect<Equal<{ wow: boolean } | undefined, typeof data>> =
true
return result
})
})
})
})
5 changes: 5 additions & 0 deletions packages/react-query/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export type UseQueryResult<
TError = unknown,
> = UseBaseQueryResult<TData, TError>

export type DefinedUseQueryResult<TData = unknown, TError = unknown> = Omit<
UseQueryResult<TData, TError>,
'data'
> & { data: TData }

export type UseInfiniteQueryResult<
TData = unknown,
TError = unknown,
Expand Down
Loading