allen
allenβ€’2y ago

Calling queries in effects

Is there any way to have a lazy query? Eg:
const myQuery = useLazyQuery(queryName);

useEffect(() => myQuery(...args), [...])
const myQuery = useLazyQuery(queryName);

useEffect(() => myQuery(...args), [...])
What I'm ultimately trying to do is defer invoking the query until the args satisfy some checks, and then I wish to debounce the query execution.
20 Replies
alexcole
alexcoleβ€’2y ago
If you don't care about reactivity, I think the easiest way to do this is with the ConvexHttpClient. You could do something like:
useEffect(() => {
const run = async () => {
const convex = new ConvexHttpClient(address);
const result = await convex.query(queryName)(...args);
}
run();
}, [...])
useEffect(() => {
const run = async () => {
const convex = new ConvexHttpClient(address);
const result = await convex.query(queryName)(...args);
}
run();
}, [...])
https://docs.convex.dev/api/classes/browser.ConvexHttpClient#query
allen
allenOPβ€’2y ago
I do care about reactivity, i just want a way to throttle when and how often the query is executed
Indy
Indyβ€’2y ago
Here's an article @ian wrote that might help with part of what you're looking for: https://stack.convex.dev/throttling-requests-by-single-flighting
Throttling Requests by Single-Flighting
For write-heavy applications, use single flighting to dynamically throttle requests. See how we implement this with React hooks for Convex.
alexcole
alexcoleβ€’2y ago
Ahh I see. And under what circumstances are you worried about it executing too often?
Indy
Indyβ€’2y ago
Actually never mind, that article is about mutations not queries πŸ™‚ Might be helpful either way.
alexcole
alexcoleβ€’2y ago
One way to defer loading the query is to use a sub-component that is conditionally mounted and load the query there. You can also throttle changing the props on the sub-component so that the query isn't re-requested too often
allen
allenOPβ€’2y ago
For more context, this is essentially a type-ahead implementation. The value being queried changes with every keystroke. I want to debounce it and also not execute it until 3 characters entered
alexcole
alexcoleβ€’2y ago
Got it. Yeah then I think having a few levels of components would solve this. Something like: Typeahead: - has state for the user input - If the input is >3 charaters mount TypeaheadResults - Throttles prop updates to TypeaheadResults TypeaheadResults: - loads results with useQuery
allen
allenOPβ€’2y ago
Yeah. seems to be getting a bit cute, especially if the component destructuring serves no other purpose. Be great to get something similar to Apollo's approach. https://www.apollographql.com/docs/react/data/queries/#manual-execution-with-uselazyquery
Apollo Docs
Queries
Fetch data with the useQuery hook
allen
allenOPβ€’2y ago
that or perhaps options that could be passed to useQuery with settings like enabled and throttle
alexcole
alexcoleβ€’2y ago
It's actually not possible if you want reactivity! Using a lazy approach like Apollo's means that you'll only get a query result when you call the getter. It doesn't give us a way to rerender with the new query result if the data on the server changes
or perhaps options that could be passed to useQuery with settings like enabled and throttle
Yep, thanks for the feedback! We've talked about adding things like this in the past and this is one more vote for doing it
allen
allenOPβ€’2y ago
Understood -- then perhaps my second proposal makes sense.
jamwt
jamwtβ€’2y ago
@allen possible some of the hints in this article are relevant: https://stack.convex.dev/help-my-app-is-overreacting. -- the overall suggestion being to use combinators to get the kind of reactivity you want. there is probably a useDebouncedQuery(ms, ...) sort of combinator that would do what you want that wraps useQuery
jamwt
jamwtβ€’2y ago
"updating too often" is in the class of things like "updating before the user is ready" that are addressed in there and in https://stack.convex.dev/coping-with-the-web-s-looming-global-reactivity-crisis
Managing Reactivity with useBufferedState
Reactivity has taken a dominant position today within web app development. Our components and app state are all reactive, and the world has adapted–mo...
jamwt
jamwtβ€’2y ago
allen
allenOPβ€’2y ago
Thanks, @jamwt. I'll take a look. Probably some reactivity patterns I just need to lean into harder.
alexcole
alexcoleβ€’2y ago
Yep hooks are another powerful pattern here! Keep in mind though that hooks can’t be called conditionally. So in @jamwt’s example, your component would always call useQuery, it just might not use the result (or use an old result) while rendering You could have a hook that denounces the arguments and uses an old version of them. But annoyingly a proper conditional hook that sometimes doesn’t load the query is impossible in React
π— π—œπ—–π—žπ—˜π—¬
I'm a bit late to this party, but I'll add another vote to get a lazyQuery hook, it'd be very useful in situations when searching. I solved it by conditionally passing the "skip" string if there are no args to pass.
ballingt
ballingtβ€’2y ago
Hey @π— π—œπ—–π—žπ—˜π—¬, can you say more about this case? Would calling the query as a one-shot work for you here, like https://docs.convex.dev/client/react#one-off-queries
const convex = useConvex();
async function eventHandler() {
console.log(await convex.query(api.foo.bar, {}));
}
const convex = useConvex();
async function eventHandler() {
console.log(await convex.query(api.foo.bar, {}));
}
π— π—œπ—–π—žπ—˜π—¬
YES! That's exactly what I was looking for! I thought I had looked everywhere in the docs for it, should've looked for "one-off" and not "lazy" πŸ˜…

Did you find this page helpful?