T
TanStack4y ago
quickest-silver

Some best practices guidelines and advice with Vue

I've only recently started using this package (not sure if I should call it vue-query or react-query) but I just wanted to know best practices in terms of query keys and data... 1. Should we try to re-use queries from composables? Or should queries be more specific to the component itself? 2. When typing the keys for the query, should the query key expect multiple Ref<> values from the component when the query is created or should my composable return these refs to the component? 3. Is it better to use ref or reactive for query key values? 4. Is there a nice way to store pagination data outside of the query cached data? So that when replacing data in queries, I know everything is just a basic array of data or is there a better way of managing query structure? I haven't gotten to this yet but we use a lot of broadcasting and need to be able to replace individual values within data.
26 Replies
quickest-silver
quickest-silverOP4y ago
5. Can I pass reactive/ref values to a query and not have to worry about unwrapping them?
exotic-emerald
exotic-emerald4y ago
yoyo almost positive its impossible to pass a ref to useQuery in any form
quickest-silver
quickest-silverOP4y ago
export const taskQueries = {
allTasksBetween: (user: string, start: Ref<Date>, end: Ref<Date>) => {
return useQuery(['tasks', user, start, end], () => fetchTasks(user, start, end), {
placeholderData: [],
});
},
};
export const taskQueries = {
allTasksBetween: (user: string, start: Ref<Date>, end: Ref<Date>) => {
return useQuery(['tasks', user, start, end], () => fetchTasks(user, start, end), {
placeholderData: [],
});
},
};
I tried this and I think it works but my UI broke for other reasons haha but starting to think that should just be in a reactive object
exotic-emerald
exotic-emerald4y ago
2) i'd check out this link (All About React Query (with Tanner Linsley) — Learn With Jason), but the answer is useQuery should likely be defined in a composable and reused the useQuery function internally calls the useBaseQuery fn, the signature looks like this arg1: | TQueryKey | UseQueryOptionsGeneric<TQueryFnData, TError, TData, TQueryKey>, arg2: | QueryFunction<TQueryFnData, UnwrapRef<TQueryKey>> | UseQueryOptionsGeneric<TQueryFnData, TError, TData, TQueryKey> = {}, arg3: UseQueryOptionsGeneric<TQueryFnData, TError, TData, TQueryKey> = {}, so looks like can't pass ref as queryKey but could be wrong at first glance
quickest-silver
quickest-silverOP4y ago
So it should always be a reactive, POJO, array or string etc?
exotic-emerald
exotic-emerald4y ago
i generally just dont think it will work hahah but haven't actually tried yet for reactive
quickest-silver
quickest-silverOP4y ago
I don't see what else you would pass?
exotic-emerald
exotic-emerald4y ago
but primitives work for sure
quickest-silver
quickest-silverOP4y ago
If you have things that are in the template like a search text-field, that's going to be v-model with a ref or reactive So it would make sense to pass that to the query as the key or part of the key?
exotic-emerald
exotic-emerald4y ago
yes, you're right but there were type issues from what I understand that prevent watch sources from being passed
quickest-silver
quickest-silverOP4y ago
I'm going to test it now ha I guess my main question was #4 How to best store pagination data so I know the structure of my query data is consistent
const search = ref('');
const { data, isLoading } = userQueries.allUsers(search);

export const userQueries = {
allUsers: (search: Ref<string>) => {
return useQuery(['users', search], fetchUsers, {
placeholderData: [],
});
},
};
const search = ref('');
const { data, isLoading } = userQueries.allUsers(search);

export const userQueries = {
allUsers: (search: Ref<string>) => {
return useQuery(['users', search], fetchUsers, {
placeholderData: [],
});
},
};
@mattyice78987 can confirm this works just fine Looks like it's unwrapped somewhere
quickest-silver
quickest-silverOP4y ago
No description
unwilling-turquoise
unwilling-turquoise4y ago
All hooks in vue-query accept refs reactive or plain values and they are unwrapped like in this function https://github.com/TanStack/query/blob/52764b0d461cbfe3acd7c0840c0477b834a9a0ac/packages/vue-query/src/useBaseQuery.ts#L104
GitHub
query/useBaseQuery.ts at 52764b0d461cbfe3acd7c0840c0477b834a9a0ac ·...
🤖 Powerful asynchronous state management, server-state utilities and data fetching for TS/JS, React, Solid, Svelte and Vue. - query/useBaseQuery.ts at 52764b0d461cbfe3acd7c0840c0477b834a9a0ac · Tan...
quickest-silver
quickest-silverOP4y ago
Amazing! So just type it appropriately and good to go!
exotic-emerald
exotic-emerald4y ago
sorry for the misdirection!
quickest-silver
quickest-silverOP4y ago
It's fine. I literally started using this yesterday and it's amazing Don't know why I haven't seen this package before
unwilling-turquoise
unwilling-turquoise4y ago
useQuery and useInfiniteQuery should have proper types already. There might be a problem with useMutation and useQueries. But this is type only problem. Functionality wise it should still work.
quickest-silver
quickest-silverOP4y ago
Probably just because I am using a composable I have to type my composable args And yeah useQuery accepts it just fine with nothing further required
unwilling-turquoise
unwilling-turquoise4y ago
Ad1. You can and probably should wrap your queries with your own compostables to encapsulate queryKey and queryFn, exposing only some necessary args. For tiny apps it might be overkill, but for bigger apps it will make reuse of specific resources much easier.
quickest-silver
quickest-silverOP4y ago
Yeah our app is huge... Hence trying to make it faster
unwilling-turquoise
unwilling-turquoise4y ago
Ad2. queryKey is an array which can contain primitives or nested objects and arrays. It should contain all arguments that you are passing to your queryFn. This way by changing args you will create new cache entry. And if you will try to hit old params, results can be served from cache first. How you will structure your queryKey is totally up to you. You can check this page for more details: https://vue-query-next.vercel.app/#/guides/query-keys
Vue Query
Description
quickest-silver
quickest-silverOP4y ago
The last piece for me before I go for a full in migration to this package is to work out the best way to handle paginated data and also nice, type safe setQueriesData so I can update multiple queries that may or may not be paginated. For example UserList (paginated) and UserSelect (select field, not paginated). We use a lot of broadcasting.
unwilling-turquoise
unwilling-turquoise4y ago
IMHO. For paginated user list i would go with something like ['users', page] or ['users', {page}]. For select field, since you have a lot of data (pagination) i would usually send denounced/throttled query to the server with user input as filter and include that in the query key, with a bit of staleTime. This way when you type the same thing the second time, you will skip trip to the server, and could serve it from cache. If it would not be used for some time, it will be GCed. The only question is if you need to display something when you did not type anything, but that could be the first page of users. I doubt anyone would scroll through thousands of users to pick one.
quickest-silver
quickest-silverOP4y ago
That was probably a contrived example and it's not that straightforward in our app. So I have the queries working fine Maybe another example would be a Task List - we have a table that lists them, paginated. Then another view that only shows for a specific date range (not paginated) So if a Task is updated somewhere else (like created and then broadcasted in), I want to update both of the above queries One is paginated, the other is not So for one, the Tasks are likely at data.items (if paginated) and the other they're just at data (if not paginated) But if the created/updated Tasks falls in the current week, well I want to put it in both queries without re-fetching Not sure if I'm explaining it poorly.
type TaskTable = {
data: TaskData[];
meta: {
total: number;
};
};
const taskTable = (page: number) =>
useQuery(['tasks', page], async (): Promise<TaskTable> => {
const response = await axios.get('/tasks', { params: { page } });

return response.data;
});

type TaskBoard = TaskData[];
const taskBoard = (start: Date, end: Date) =>
useQuery(['tasks', start, end], async (): Promise<TaskBoard> => {
const response = await axios.get('/tasks', { params: { start, end } });

return response.data.data;
});
type TaskTable = {
data: TaskData[];
meta: {
total: number;
};
};
const taskTable = (page: number) =>
useQuery(['tasks', page], async (): Promise<TaskTable> => {
const response = await axios.get('/tasks', { params: { page } });

return response.data;
});

type TaskBoard = TaskData[];
const taskBoard = (start: Date, end: Date) =>
useQuery(['tasks', start, end], async (): Promise<TaskBoard> => {
const response = await axios.get('/tasks', { params: { start, end } });

return response.data.data;
});
Being able to update both of the above queries with type safety when using setQueriesData is what I'm trying to do If that helps explain my goal 🙂 I suppose the other option is to enforce API responses so that I should expect a meta key on every response - even if not paginated
unwilling-turquoise
unwilling-turquoise4y ago
So there are two concepts you could use: https://vue-query-next.vercel.app/#/guides/query-invalidation https://vue-query-next.vercel.app/#/guides/optimistic-updates From what I understand you are trying to achieve optimistic update. I think in this case you need to figure out what works best for your data model. As every use case has it own quirks.
Vue Query
Description
quickest-silver
quickest-silverOP4y ago
Yeah but updating two queries with differing structures is what I'm trying to solve Not as simple as just applying the same updater function to both https://github.com/TanStack/query/discussions/1780 This discussion is similar to what I want to do I think I either have to have the same structure Or somehow track the structure of the query Like being able to tag if it's paginated or not

Did you find this page helpful?