I am using React Query to fetch data from an API in a React app. I want to implement debounce for better performance, but I'm having trouble getting it to work with useQuery. When I try to wrap my API call in a debounced function, I get an error saying "query function must return a defined value".
Here is the code I am currently using:
async function fetchProducts() {
const response = await axios.get(`/api/products?category_id=${category_id}&${searchParams.toString()}&page=${page}`);
return response.data;
}
const debouncedFetchProducts = React.useMemo(
() => _.debounce(fetchProducts, 500),
[fetchProducts]
);
// The following queries will execute in parallel
const categoryQuery = useQuery({ queryKey: ['category'], queryFn: fetchCategory, keepPreviousData: true });
const productsQuery = useQuery({ queryKey: ['products', category_id, sortPrice, minPrice, maxPrice, page, categoryFilters], queryFn: debouncedFetchProducts, keepPreviousData: true, staleTime: 1000 });
When I run this, I get an error saying "query function must return a defined value". I believe this is because the debounced function returns a promise, but useQuery expects an actual value.
I have also tried using useAsync, but I would like to use useQuery because it has built-in caching.
Can someone help me figure out how to implement debounce with useQuery in React Query?
Thank you in advance for your help!
You can utilize the useDebounce hook to trigger a queryKey
update in react-query instead of using the debounce
function from the Underscore library.
For example:
const [searchParams, setSearchParams] = useDebounce([category_id, sortPrice, minPrice, maxPrice, page, categoryFilters], 1000)
const productsQuery = useQuery({ queryKey: ['products', ...searchParams], queryFn: fetchProducts, keepPreviousData: true, staleTime: 1000 });
useDebounce
is applied to the searchParams array, which includes variables like category_id, sortPrice, minPrice, maxPrice, page, and categoryFilters.
The debounce delay is set to 1000 milliseconds (1 second). The productsQuery then uses the debounced search parameters in its query key, ensuring that the fetchProducts
function is only called when the debounced search parameters change.
You can find a working useDebounce
example in this codesandbox example
I used the AbortSignal
parameter with a sleep()
on a useMutation
to replicate debounce behaviour ootb. It should be the same for useQuery
.
useQuery
is re-triggered, react-query cancels the previous inflight request (based on queryKey
).useQuery
implementation below, waits half a second, and then checks whether the signal
(i.e. this request attempt) has been aborted.signal
through to the axios request - that way the request (if somehow inflight) can be cancelled. Useful for big payloads!e.g.
const sleep = (ms = 1000) => new Promise(resolve => setTimeout(resolve, ms))
const categoryQuery = useQuery({
queryKey: ['category'],
queryFn: async ({ signal }) => {
await sleep(500)
if (!signal?.aborted) {
const response = await axios.get(`/api/products?category_id=${category_id}&${searchParams.toString()}&page=${page}`,
{ signal });
return response.data;
}
},
keepPreviousData: true
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With