The problem is : if i change start and search in the same time, two queries are sent, but I just want one.
const [start, setStart] = useState(0);
const [search, setSearch] = useState('')
const { data: usersData, error: usersError, isLoading: usersIsLoading, isError: usersIsError } = useQuery({
queryKey: ['users', start, search],
queryFn: () => getUsers(start, search)
});
The solution I found is to use a debounce function :
const [start, setStart] = useState(0);
const [search, setSearch] = useState('');
const debouncedStart = useValueDebounce(start, 500);
const debouncedSearch = useValueDebounce(search, 500);
function useValueDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
const { data: usersData, error: usersError, isLoading: usersIsLoading, isError: usersIsError } = useQuery({
queryKey: ['users', debouncedStart, debouncedSearch],
queryFn: () => getUsers(limit, debouncedStart, debouncedSearch)
});
Is there a better way to do that ?
1
This looks to me like a good approach to avoid triggering multiple queries in rapid succession.
What also would be possible to optimize, is by leveraging libraries like React Query’s useQuery hook, which natively supports a staleTime option or refetch triggers.
If debouncing is still required, consider using a more minimal approach without custom hooks or integrating a more specialized debounce library like lodash.
A possible improvement without custom debounce logic could be:
- Use Lodash Debounce
Lodash’s debounce function is widely used and reliable for these scenarios.
import { debounce } from 'lodash';
import { useQuery } from 'react-query';
const [start, setStart] = useState(0);
const [search, setSearch] = useState('');
const fetchUsers = useCallback(debounce((start, search) => getUsers(start, search), 500), []);
const { data: usersData, error: usersError, isLoading: usersIsLoading, isError: usersIsError } = useQuery({
queryKey: ['users', start, search],
queryFn: () => fetchUsers(start, search),
staleTime: 500 // Optional: time to consider data fresh, avoids refetching
});
- Refine Debouncing Strategy
Another way is to make sure that you debounce the setter functions for start and search only, which would be slightly more performant:
const debouncedSetStart = useCallback(debounce(setStart, 500), []);
const debouncedSetSearch = useCallback(debounce(setSearch, 500), []);
This for example, avoids the need for wrapping the entire query in a debounce handler, allowing the state updates to be controlled precisely, reducing unnecessary renders and queries.
Both approaches aim to improve maintainability and avoid custom debounce hooks unless really needed.
I hope I could help somehow,
K.