Skip to content
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

Feature/query options #5153

Merged
merged 11 commits into from
Apr 30, 2023
33 changes: 33 additions & 0 deletions docs/react/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ if (axios.isAxiosError(error)) {
[typescript playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgVwM4FMCKz1QJ5wC+cAZlBCHAOQACMAhgHaoMDGA1gPRTr2swBaAI458VALAAoUJFhx6AD2ARUpcpSqLlqCZKkw8YdHADi5ZGDgBeRHGAATAFxxGyEACNcRKVNYRm8CToMKwAFmYQFqo2ABQAlM4ACurAGAA8ERYA2gC6AHzWBVoqAHQA5sExVJxl5mA6cSUwoeiMMTyokMzGVgUdXRgl9vQMcT6SfgG2uORQRNYoGNi4eDFIIisA0uh4zllUtZH1VDkANHAb+ABijM5BIeF1qoRjkpyccJ9fAHoA-OPAEhwGLFVAlVIAQSUKgAolBZjEZtA4nFEFJPkioOi4O84H8pIQgA)
[//]: # 'Playground6'

A neat trick that also works is specifying an empty `onError` handler just to get type inference for the error field:

```tsx
import axios, { AxiosError } from 'axios'

const { error } = useQuery({
queryKey: ['groups'],
queryFn: fetchGroups,
onError: (_error: AxiosError) => {},
})
error
// ^? const error: AxiosError | null
```

### Registering a global Error

TanStack Query v5 allows for a way to set a global Error type for everything, without having to specify generics on call-sides, by amending the `Register` interface. This will make sure inference still works, but the error field will be of the specified type:
Expand All @@ -140,6 +154,25 @@ const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })

[//]: # 'Materials'

## Typing Query Options

If you inline query options into `useQuery`, you'll get automatic type inference. However, you might want to extract the query options into a separate function to share them between `useQuery` and e.g. `prefetchQuery`. In that case, you'd lose type inference. To get it back, you can use `queryOptions` helper:

```ts
import { queryOptions } from '@tanstack/react-query'

function groupOptions() {
return queryOptions({
queryKey: ['groups'],
queryFn: fetchGroups,
staleTime: 5 * 1000,
})
}

useQuery(groupOptions())
queryClient.prefetchQuery(groupOptions())
```

## Further Reading

For tips and tricks around type inference, have a look at [React Query and TypeScript](../community/tkdodos-blog#6-react-query-and-typescript) from
Expand Down
22 changes: 21 additions & 1 deletion packages/react-query/src/__tests__/useQuery.types.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useQuery } from '../useQuery'
import { queryOptions, useQuery } from '../useQuery'
import type { Expect, Equal } from './utils'
import { doNotExecute } from './utils'

Expand All @@ -23,6 +23,26 @@ describe('initialData', () => {
})
})

it('TData should be defined when passed through queryOptions', () => {
doNotExecute(() => {
const options = queryOptions({
queryKey: ['key'],
queryFn: () => {
return {
wow: true,
}
},
initialData: {
wow: true,
},
})
const { data } = useQuery(options)

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({
Expand Down
2 changes: 1 addition & 1 deletion packages/react-query/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export * from '@tanstack/query-core'
export * from './types'
export { useQueries } from './useQueries'
export type { QueriesResults, QueriesOptions } from './useQueries'
export { useQuery } from './useQuery'
export { useQuery, queryOptions } from './useQuery'
export {
QueryClientContext,
QueryClientProvider,
Expand Down
22 changes: 22 additions & 0 deletions packages/react-query/src/useQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,28 @@ import type {
} from './types'
import { useBaseQuery } from './useBaseQuery'

export function queryOptions<
TQueryFnData = unknown,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is query specific, we would need similar helper for infinite, mutation and queries?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably not necessary for mutations, but for infinite: yes

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't even have the initialData overload for infiniteQuery 😅

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a separate helper in a follow-up. This is not breaking so we can add it at any time I think

TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>

export function queryOptions<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
): DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>

export function queryOptions(options: unknown) {
return options
}
Comment on lines +11 to +31
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move this to the core, @DamianOsipiuk ?

The options are currently based on UseQueryOptions, which only exists in react-query. I think each lib has their own impl over QueryObserverOptions 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this would not work as in Vue we have to wrap every option as a possible ref
https://github.com/TanStack/query/blob/alpha/packages/vue-query/src/useQuery.ts#L16

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay then let's let every framework export their own queryOptions ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, i think that would be necessary.


// HOOK
type UndefinedInitialDataOptions<
TQueryFnData = unknown,
Expand Down