如何在React Query的useQuery中实现防抖?解决返回值报错
在React Query中实现防抖的正确方式
你遇到的错误根源是:直接用lodash的debounce包装fetchProducts后传给useQuery的queryFn行不通。因为debounce返回的函数被调用时,不会立即执行原异步函数,而是延迟执行,这会导致useQuery调用queryFn时得到的返回值是undefined,不符合queryFn必须返回Promise的要求。
要在保留React Query缓存功能的前提下实现防抖,正确思路是防抖触发查询的参数变化,而非防抖API调用函数本身。具体实现步骤如下:
1. 保存防抖后的参数状态
用useState存储经过防抖处理的查询参数,只有当参数稳定一段时间后,才更新这个状态,进而触发React Query的重新查询。
2. 监听原始参数变化,防抖更新状态
通过useEffect监听原始筛选/分页参数(比如category_id、sortPrice等)的变化,用防抖逻辑延迟更新防抖后的参数状态。
3. 基于防抖后的参数执行查询
将防抖后的参数作为queryKey的一部分,并在API调用中使用这些参数,确保只有参数稳定后才发起请求。
以下是修改后的完整代码示例:
// 假设这些是组件中用于筛选/分页的原始状态 // const [category_id, setCategoryId] = useState(...); // const [sortPrice, setSortPrice] = useState(...); // ...其他参数 // 存储防抖后的参数 const [debouncedParams, setDebouncedParams] = React.useState({ category_id, sortPrice, minPrice, maxPrice, categoryFilters, page }); // 创建防抖的参数更新函数 const updateDebouncedParams = React.useMemo(() => _.debounce((newParams) => { setDebouncedParams(newParams); }, 500), []); // 监听原始参数变化,触发防抖更新 React.useEffect(() => { updateDebouncedParams({ category_id, sortPrice, minPrice, maxPrice, categoryFilters, page }); // 组件卸载或参数变化时取消未执行的防抖任务 return () => updateDebouncedParams.cancel(); }, [category_id, sortPrice, minPrice, maxPrice, categoryFilters, page, updateDebouncedParams]); // API调用函数,使用防抖后的参数 async function fetchProducts() { const { category_id, sortPrice, minPrice, maxPrice, categoryFilters, page } = debouncedParams; const searchParams = new URLSearchParams(); // 根据防抖后的参数构建查询字符串 if (sortPrice) searchParams.set('sortPrice', sortPrice); if (minPrice) searchParams.set('minPrice', minPrice); if (maxPrice) searchParams.set('maxPrice', maxPrice); Object.entries(categoryFilters).forEach(([key, value]) => { if (value) searchParams.set(key, value); }); const response = await axios.get(`/api/products?category_id=${category_id}&${searchParams.toString()}&page=${page}`); return response.data; } // 使用React Query发起查询,queryKey基于防抖后的参数 const categoryQuery = useQuery({ queryKey: ['category'], queryFn: fetchCategory, keepPreviousData: true }); const productsQuery = useQuery({ queryKey: ['products', debouncedParams], // React Query会自动浅比较对象变化 queryFn: fetchProducts, keepPreviousData: true, staleTime: 1000 });
方案优势
- 只有当原始参数稳定500ms后,才会触发React Query的重新查询,实现了防抖效果。
- 完全保留了React Query的缓存、后台重新获取、
keepPreviousData等核心特性,不会因为防抖丢失这些功能。
内容的提问来源于stack exchange,提问作者Tiramisu
相关产品推荐
相关产品推荐

