feat(vue-query): add support for getters in query keys
This is a based on the discussion I found in https://github.com/TanStack/query/discussions/5990. According to the author of the PR which made enabled a getter, they stated they didn't find a use case for query keys so it was not implemented.
Personally, I have found many use cases for getters inside of query keys. Commonly, I'll define composables for my API and then have them accept reactive variables like so:
export function useProjectDetailsQuery(userId: MaybeRef<string>, projectId: MaybeRef<string>) {
return useQuery({
queryKey: ['users', userId, 'projects', projectId, 'details'],
queryFn: () => api.fetchProjectDetails(toValue(userId), toValue(projectId))
})
}
When I use these composable I often have to make intermediate computed variables to keep the data from props reactive:
import { useQuery } from '@tanstack/vue-query';
export interface Props {
user: User;
project: Project;
}
const props = defineProps<Props>();
const { data: project } = useProjectDetailsQuery(
computed(() => props.user.id),
computed(() => props.project.id),
);
With this PR the we don't need intermediate computed properties to do this, you can write the code using getters instead:
const { data: project } = useProjectDetailsQuery(
() => props.user.id,
() => props.project.id,
);
This also makes composables like the one I defined above fall more into line with the recommended practices for vue composables https://vuejs.org/guide/reusability/composables#input-arguments.
CC @DamianOsipiuk
The latest updates on your projects. Learn more about Vercel for Git βοΈ
1 Ignored Deployment
| Name | Status | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| query | β¬οΈ Ignored (Inspect) | Visit Preview | Jul 1, 2024 11:00pm |
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
Latest deployment of this branch, based on commit b2ed0fcb5c0f4f4aac669f4c121e21245394eb2d:
| Sandbox | Source |
|---|---|
| @tanstack/query-example-angular-basic | Configuration |
| @tanstack/query-example-react-basic-typescript | Configuration |
| @tanstack/query-example-solid-basic-typescript | Configuration |
| @tanstack/query-example-svelte-basic | Configuration |
| @tanstack/query-example-vue-basic | Configuration |
βοΈ Nx Cloud Report
CI is running/has finished running commands for commit dc3b6cc41e93c14d73924a0fa55b95a1845e515e. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.
π See all runs for this CI Pipeline Execution
β Successfully ran 2 targets
Sent with π from NxCloud.
Codecov Report
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 71.42%. Comparing base (
d2a92d9) to head (dc3b6cc). Report is 3 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #7608 +/- ##
===========================================
+ Coverage 44.46% 71.42% +26.96%
===========================================
Files 185 19 -166
Lines 7049 462 -6587
Branches 1549 119 -1430
===========================================
- Hits 3134 330 -2804
+ Misses 3552 102 -3450
+ Partials 363 30 -333
This could improve ergonomics, but solution is insufficient.
QueryKey can be infinitely nested structure, so checking only the top level of QueryKey would not resolve deeply nested getters. This in turn will break query key serialization.
We would need to create a variant of cloneDeepUnref that will unwrap getters as well, but we would need to run this only on QueryKey as other parameters might be functions on purpose.
This could improve ergonomics, but solution is insufficient.
QueryKey can be infinitely nested structure, so checking only the top level of QueryKey would not resolve deeply nested getters. This in turn will break query key serialization.
We would need to create a variant of
cloneDeepUnrefthat will unwrap getters as well, but we would need to run this only onQueryKeyas other parameters might be functions on purpose.
Ok thank you for your feedback on this. I'll look into how I can conditionally deep unwrap getters specifically for the query key.
@DamianOsipiuk I think I've managed to get nested getter functions to work. The new and old tests are passing so I (assume?) nothing broke in the process. Basically I added a level and key parameter to the customize callback to detect if we hit the top level queryKey. For useQueries I've refactored it to first unref the top level array and then process each element the same way useQuery process it's options.
I'm pretty confident useQuery will work as expected, I just wanted to verify with you if the changes to useQueries are ok.
Run & review this pull request in StackBlitz Codeflow.
commit: dc3b6cc
@tanstack/angular-query-devtools-experimental
npm i https://pkg.pr.new/@tanstack/angular-query-devtools-experimental@7608
@tanstack/angular-query-experimental
npm i https://pkg.pr.new/@tanstack/angular-query-experimental@7608
@tanstack/eslint-plugin-query
npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@7608
@tanstack/query-async-storage-persister
npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@7608
@tanstack/query-broadcast-client-experimental
npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@7608
@tanstack/query-core
npm i https://pkg.pr.new/@tanstack/query-core@7608
@tanstack/query-devtools
npm i https://pkg.pr.new/@tanstack/query-devtools@7608
@tanstack/query-persist-client-core
npm i https://pkg.pr.new/@tanstack/query-persist-client-core@7608
@tanstack/query-sync-storage-persister
npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@7608
@tanstack/react-query
npm i https://pkg.pr.new/@tanstack/react-query@7608
@tanstack/react-query-devtools
npm i https://pkg.pr.new/@tanstack/react-query-devtools@7608
@tanstack/react-query-next-experimental
npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@7608
@tanstack/react-query-persist-client
npm i https://pkg.pr.new/@tanstack/react-query-persist-client@7608
@tanstack/solid-query
npm i https://pkg.pr.new/@tanstack/solid-query@7608
@tanstack/solid-query-devtools
npm i https://pkg.pr.new/@tanstack/solid-query-devtools@7608
@tanstack/solid-query-persist-client
npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@7608
@tanstack/svelte-query
npm i https://pkg.pr.new/@tanstack/svelte-query@7608
@tanstack/svelte-query-devtools
npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@7608
@tanstack/svelte-query-persist-client
npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@7608
@tanstack/vue-query
npm i https://pkg.pr.new/@tanstack/vue-query@7608
@tanstack/vue-query-devtools
npm i https://pkg.pr.new/@tanstack/vue-query-devtools@7608
templates
- @tanstack/query-example-angular-basic
- @tanstack/query-example-angular-infinite-query-with-max-pages
- @tanstack/query-example-angular-router
- @tanstack/query-example-angular-simple
- @tanstack/query-example-react-algolia
- @tanstack/query-example-react-auto-refetching
- @tanstack/query-example-react-basic
- @tanstack/query-example-react-basic-graphql-request
- @tanstack/query-example-react-basic-typescript
- @tanstack/query-example-react-default-query-function
- @tanstack/query-example-react-infinite-query-with-max-pages
- @tanstack/query-example-react-load-more-infinite-scroll
- @tanstack/query-example-react-nextjs
- @tanstack/query-example-react-nextjs-app-prefetching
- @tanstack/query-example-nextjs-suspense-streaming
- @tanstack/query-example-react-offline
- @tanstack/query-example-react-optimistic-updates-cache
- @tanstack/query-example-react-optimistic-updates-ui
- @tanstack/query-example-react-pagination
- @tanstack/query-example-react-playground
- @tanstack/query-example-react-prefetching
- @tanstack/query-example-react-react-native
- @tanstack/query-example-react-router
- @tanstack/query-example-react-rick-morty
- @tanstack/query-example-react-shadow-dom
- @tanstack/query-example-react-simple
- @tanstack/query-example-react-star-wars
- @tanstack/query-example-react-suspense
- @tanstack/query-example-solid-astro
- @tanstack/query-example-solid-basic-graphql-request
- @tanstack/query-example-solid-basic-typescript
- @tanstack/query-example-solid-default-query-function
- @tanstack/query-example-solid-simple
- @tanstack/query-example-solid-start-streaming
- @tanstack/query-example-svelte-auto-refetching
- @tanstack/query-example-svelte-basic
- @tanstack/query-example-svelte-load-more-infinite-scroll
- @tanstack/query-example-svelte-optimistic-updates-typescript
- @tanstack/query-example-svelte-playground
- @tanstack/query-example-svelte-simple
- @tanstack/query-example-svelte-ssr
- @tanstack/query-example-svelte-star-wars
- @tanstack/query-example-vue-2.6-basic
- @tanstack/query-example-vue-2.7-basic
- @tanstack/query-example-vue-basic
- @tanstack/query-example-vue-dependent-queries
- @tanstack/query-example-vue-nuxt3
- @tanstack/query-example-vue-persister
- @tanstack/query-example-vue-simple