T
TanStack6mo ago
frail-apricot

Is there a way to have a reactive array of queries?

Hey, we are implementing a chat with vue-query, and the problem is that we need an unread count indicator. Currently we have a query with ['conversations', uuid] key, and would like to have like a computed with all the queries that starts with ['conversations'] and iterate over them and count unread conversations. Is this something possible? I tried getQueriesData but that seems to be not reactive.
6 Replies
foreign-sapphire
foreign-sapphire6mo ago
Really proper way to achieve this is to have a server endpoint, that could give you this information directly, So you may just have ['conversations', 'unread-count'] query for example. But in the case when you don't have control over the server API you may use useQueries api.
// somehow get the list of all uuids you're interested in
const uuids = computed(() => /**/);

// using queryOptions API your queryKey and queryFn can be reused for both `useQuery` and `useQueries` with full type-safety
const options = computed(() => {
return uuids.map(uuid => {
return queryOptions({
queryKey: ['conversations', uuid],
queryFn: getConversation(uuid),
})
});
});

const queries = useQueries(options);
const totalUnread = computed(() => {
const result = 0;
for (const query in queries.value) {
const converstaion = query.data;
if (!converstaion) continue;
results += converstaion.unreadCount;
}
return result;
});
// somehow get the list of all uuids you're interested in
const uuids = computed(() => /**/);

// using queryOptions API your queryKey and queryFn can be reused for both `useQuery` and `useQueries` with full type-safety
const options = computed(() => {
return uuids.map(uuid => {
return queryOptions({
queryKey: ['conversations', uuid],
queryFn: getConversation(uuid),
})
});
});

const queries = useQueries(options);
const totalUnread = computed(() => {
const result = 0;
for (const query in queries.value) {
const converstaion = query.data;
if (!converstaion) continue;
results += converstaion.unreadCount;
}
return result;
});
Data will be reactive and you also don't need to worry about over-fetching. Data in useQuery and useQueries will be shared and promises will be de-duplicated.
frail-apricot
frail-apricotOP6mo ago
Thanks @wlnt, we could do a server endpoint, but we have mechanism to update these data through websockets, and we don't want to update multiple queries when a message arrives if that is possible. I have tried useQueries before, but that way I still need a list of conversation uuids somewhere to be able to map over them. But I will try it again with this approach! Also I have seen you are really active in the vue-query questions and seems to be a pro in this field, are you able for a paid call where you could give us directions how we should implement a few things? We are switching from pinia and I think we still need a mind shift to understood how we should handle data after the switch!
foreign-sapphire
foreign-sapphire6mo ago
Got it. There's another workaround I see. You can have a global state and a composable per each query that will contribute to that state.
We can talk about the call details in the DMs. Feel free to message me.
frail-apricot
frail-apricotOP6mo ago
DM sent!
ratty-blush
ratty-blush4mo ago
I am trying to do something sort of the inverse of this... I have a bunch of different data like flash cards and phrases/translations, and they'll be stashed in big chunks like ['user', language_name] (referring to the user's data relating to the language they're learning; 1 at a time). And that big thing will store all the phrases, and if I want just one phrase I can do a useQuery with a select, pass a language name to say which cache entry to get it from, and pass the uuid of the phrase for use in a simple select function like select: data => data.phrasesMap[uuid], and I'm good to go: const { data } = useOnePhrase(lang, phrase_uuid) The problem is, I end up passing around this "lang" value a lot, and prop-drilling, and accessing params and props and context and so on. But every phrase has a uuid, and so does every card and review and all the other things. I find myself wishing there were some central registry where I could just do something like const data = useAnyObjectInTheCache(uuid) and it would just get it. I know I could achieve this by removing the language name from the cache key, but then it's not possible to invalidate just one language at a time. Or I could cache the big chunk and then loop over its different datapoints and cache each object on its own key (e.g. queryKey: ['objects', uuid]) but then invalidation becomes an issue because if I invalidate all the cards under a certain language, I then have to also loop over all the cards and invalidate everything in the uuid storage space too... or maybe there is some simpler way to achieve this?
foreign-sapphire
foreign-sapphire4mo ago
So passing language around is a problem? You can pull that state higher and provide/inject it possibly If you can show some code that will help to better understand your issue.

Did you find this page helpful?