useQuery local cache
Hi! I'm trying to figure out if there's any local caching with
useQuery
... I've setup a basic example where I make a query based on an ID in the URL and it looks like it's making a request to the backend every time: https://share.filip.sh/XBbZ2nxm. As you can see, the title is flashing on each route change and I log "log from db", which also happens every time.
Frontend:
Backend:
I found this section in the docs, but it sounds like that's about caching on the server? https://docs.convex.dev/functions/query-functions#caching--reactivityQueries | Convex Developer Hub
Queries are the bread and butter of your backend API. They fetch data from the
11 Replies
Hi @filipstefansson, the caching in the Convex client is predictable but explicit: if you want to remain subscribed to a query even when not viewing the data you need to subscribe to the same query elsewhere.
Getting more advanced, if you want to stay subscribed to previously loaded data or preload data you can create a Watch on a query and set a timer to release it. https://docs.convex.dev/api/interfaces/react.Watch
Interface: Watch | Convex Developer Hub
react.Watch
Ideally I'd like it to work like react-query, if you make the same query twice (with the same params), it returns the cached data first (from the first query) and then hits the backend.
That would help with the flickering when going back to a previously visited page.
But it sounds like I could achive that using Watch?
That's right, either when your component renders or when you hover it you could subscribe and stay subscribed for an interval, here's a hover:
Where react-query maintains a cache of possibly inconsistent/stale data, Convex will only every show up-to-date data.
You can either go explicit with Watch etc, using your knowledge of your app's information architecture to decide which queries to stay subscribed to, or you can write your own
useStaleQuery
hook that uses a global cache.
Note re
if you make the same query twice (with the same params), it returns the cached data first (from the first query) and then hits the backend.With Convex if you make two queries concurrently you will get this behavior, that's how the suggestion of staying subscribed by adding a Watch works: there is a global cache, it's just never stale. And instead of implicit LRU-style caching, it's explicit: you create Watches on data you want to stay subscribed to, and the React bindings automatically subscribe and unsubscribe when components are mounted and unmounted.
Hey @filipstefansson check out the useStableQuery in this article: https://stack.convex.dev/help-my-app-is-overreacting
It caches the result via
useState
, so if you're not unmounting your component you'll get no flash.
Let us know if you are, we can help you write one that uses global in-memory store or SessionStorage.Help, my app is overreacting!
Reactive backends like Convex make building live-updating apps a cinch, but default behavior might be too reactive for some use cases. Not to worry! L...
@filipstefansson ☝️ Michal's solution here is slicker, check this out! It should be rare that you need the global cache that I spent 200 words talking about
I think this might actually be the article @Michal Srb was referring to: https://stack.convex.dev/help-my-app-is-overreacting
Help, my app is overreacting!
Reactive backends like Convex make building live-updating apps a cinch, but default behavior might be too reactive for some use cases. Not to worry! L...
Since I'm switching routes, there's a full unmount when I leave the page and then a mount when coming back to the route, so it's not re-renders that's the problem I'm having.
useStableQuery
does a local cache, but I think I need a global cache. I named the thread "local cache" because I meant on the client and not on the server/db, so sorry for that confusion.
I guess I could create a useStableQuery
but one that writes to a global cache?
In shory, my question is:
Do you have anything like this planned:
or do we have to roll our own global cache solution?Ah, yeah if you want a global cache that can be stale you should roll your own.
If you want a global subscription (like a cache but it stays up to date) that's the
Watch
approach, the query key used is api.threads.get
combined with the the serialized arguments object {thread: id}
.
There are no immediate plans for a global cache that allows stale values but it's helpful to hear this is something you'd like. There are a variety of ways to manage this cache (when to expire, whether to refresh occasionally, how to evict due to memory limits) and there's no default we're providing because we prefer the subscriptions approach. I'd suggest trying subscriptions but if notes are changing quickly and you don't want to be notified of changes to notes not currently in view then the global stale cache seems appropriate.@filipstefansson here's roughly how to do it, instead of a ref you want a global object that survives your route navigation. The ConvexReactClient wrapping your app should be one such object:
@Michal Srb Thank you for the provided useStableQueryOverMounts example, and sorry for just jumping in there.
I have a question, as I'm trying to implement it in my app. I'm doing a to do app, where as I will have different boardIds. Should I include the boardId in the key, or is it enough like this:
You should include the boardId in the key. You can change the hook implementation to do this automatically for you (you could even get rid of the custom key and generate the key from the function name and arguments, if you're ok persisting the same results from all callsites)