Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use debounce with useQuery in React Query?

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!

like image 591
Tiramisu Avatar asked Aug 31 '25 10:08

Tiramisu


2 Answers

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

like image 50
Cody Chang Avatar answered Sep 03 '25 01:09

Cody Chang


I used the AbortSignal parameter with a sleep() on a useMutation to replicate debounce behaviour ootb. It should be the same for useQuery.

Explanation:

  • whenever a useQuery is re-triggered, react-query cancels the previous inflight request (based on queryKey).
  • the useQuery implementation below, waits half a second, and then checks whether the signal (i.e. this request attempt) has been aborted.
  • if not aborted, proceed!

One more thing...

  • pass the 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
});

More information:

  • https://tanstack.com/query/v4/docs/framework/react/guides/query-cancellation
  • https://axios-http.com/docs/cancellation

Related:

  • https://github.com/TanStack/query/issues/293#issuecomment-2124787215
like image 25
Nick Grealy Avatar answered Sep 03 '25 00:09

Nick Grealy