Skip to content

Commit 0e3f15b

Browse files
authored
feat: queryOptions helper function (#5153)
* feat: queryOptions helper function so that we can define and share options outside of useQuery, but still get the type inference * docs: queryOptions * docs: remove reference to onError * docs: update migration guide
1 parent f74f755 commit 0e3f15b

File tree

5 files changed

+67
-2
lines changed

5 files changed

+67
-2
lines changed

docs/react/guides/migrating-to-v5.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,10 @@ You can adjust the `maxPages` value according to the UX and refetching performan
439439

440440
Note that the infinite list must be bi-directional, which requires both `getNextPageParam` and `getPreviousPageParam` to be defined.
441441

442+
### Typesafe way to create Query Options
443+
444+
See the [Typescript Docs](../typescript#typing-query-options) for more details.
445+
442446
### CreateStore
443447

444448
We are now exposing a way to customize how queries are stored internally. Per default, a `Map` is used but, with the new `createStore` function, you can now use any data structure you want.

docs/react/typescript.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
140140

141141
[//]: # 'Materials'
142142

143+
## Typing Query Options
144+
145+
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:
146+
147+
```ts
148+
import { queryOptions } from '@tanstack/react-query'
149+
150+
function groupOptions() {
151+
return queryOptions({
152+
queryKey: ['groups'],
153+
queryFn: fetchGroups,
154+
staleTime: 5 * 1000,
155+
})
156+
}
157+
158+
useQuery(groupOptions())
159+
queryClient.prefetchQuery(groupOptions())
160+
```
161+
143162
## Further Reading
144163

145164
For tips and tricks around type inference, have a look at [React Query and TypeScript](../community/tkdodos-blog#6-react-query-and-typescript) from

packages/react-query/src/__tests__/useQuery.types.test.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useQuery } from '../useQuery'
1+
import { queryOptions, useQuery } from '../useQuery'
22
import type { Expect, Equal } from './utils'
33
import { doNotExecute } from './utils'
44

@@ -23,6 +23,26 @@ describe('initialData', () => {
2323
})
2424
})
2525

26+
it('TData should be defined when passed through queryOptions', () => {
27+
doNotExecute(() => {
28+
const options = queryOptions({
29+
queryKey: ['key'],
30+
queryFn: () => {
31+
return {
32+
wow: true,
33+
}
34+
},
35+
initialData: {
36+
wow: true,
37+
},
38+
})
39+
const { data } = useQuery(options)
40+
41+
const result: Expect<Equal<{ wow: boolean }, typeof data>> = true
42+
return result
43+
})
44+
})
45+
2646
it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => {
2747
doNotExecute(() => {
2848
const { data } = useQuery({

packages/react-query/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export * from '@tanstack/query-core'
77
export * from './types'
88
export { useQueries } from './useQueries'
99
export type { QueriesResults, QueriesOptions } from './useQueries'
10-
export { useQuery } from './useQuery'
10+
export { useQuery, queryOptions } from './useQuery'
1111
export {
1212
QueryClientContext,
1313
QueryClientProvider,

packages/react-query/src/useQuery.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,28 @@ import type {
88
} from './types'
99
import { useBaseQuery } from './useBaseQuery'
1010

11+
export function queryOptions<
12+
TQueryFnData = unknown,
13+
TError = unknown,
14+
TData = TQueryFnData,
15+
TQueryKey extends QueryKey = QueryKey,
16+
>(
17+
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
18+
): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>
19+
20+
export function queryOptions<
21+
TQueryFnData = unknown,
22+
TError = unknown,
23+
TData = TQueryFnData,
24+
TQueryKey extends QueryKey = QueryKey,
25+
>(
26+
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
27+
): DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>
28+
29+
export function queryOptions(options: unknown) {
30+
return options
31+
}
32+
1133
// HOOK
1234
type UndefinedInitialDataOptions<
1335
TQueryFnData = unknown,

0 commit comments

Comments
 (0)