Instead of debouncing the fetch function directly, you should debounce the query parameters (e.g., the search value) so that the queryKey changes only after a delay. This way useQuery is called only when the input has stabilized, not on every keystroke.
1. Create a useDebounce Hook
This hook will delay the value and only set the debounced value after the specified delay:
const handler = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
2. Use Debounced Value in Your Query Component
Now use useDebounce with useQuery like this:
const debouncedSearch = useDebounce(search, 500);
const { data, isLoading, isError } = useQuery({
queryKey: [‘todos’, page, debouncedSearch],
queryFn: () => fetch(`/api/todos?page=${page}&q=${debouncedSearch}`)
.then(res => res.json()),
enabled: !!debouncedSearch,
});
This way the query will only run after the debounced value changes
Also Read: How Much Does It Cost to Hire Remote React Developers?
Why This Works
- Debouncing the queryKey instead of the query function matches React Query’s caching and lifecycle logic.
- useQuery will only trigger when debouncedSearch changes, not on every keystroke.
- You still get all the benefits of caching, isLoading, isError and other React Query states