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
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,
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