Skip to content

Commit

Permalink
feat: queryOptions helper function (#5153)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
TkDodo authored Apr 30, 2023
1 parent f74f755 commit 0e3f15b
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 2 deletions.
4 changes: 4 additions & 0 deletions docs/react/guides/migrating-to-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ You can adjust the `maxPages` value according to the UX and refetching performan

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

### Typesafe way to create Query Options

See the [Typescript Docs](../typescript#typing-query-options) for more details.

### CreateStore

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.
Expand Down
19 changes: 19 additions & 0 deletions docs/react/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,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 @@ -8,6 +8,28 @@ import type {
} from './types'
import { useBaseQuery } from './useBaseQuery'

export function queryOptions<
TQueryFnData = unknown,
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
}

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

0 comments on commit 0e3f15b

Please sign in to comment.