React中debounce防抖未实现每秒最多执行一次请求问题排查
问题根因
- 每次组件重渲染都会生成独立的
debounce实例:你将handleQuery直接定义在组件函数体内,只要value变更触发组件重渲染,就会创建全新的防抖函数,每个函数都持有独立的定时器状态,无法继承上一次渲染的防抖逻辑,自然无法合并多次触发的请求。 useEffect监听value变化就会触发执行,每次拿到的都是新创建的防抖实例,相当于每次输入都单独启动了一个1000ms的定时器,最终所有请求都只是延迟了1000ms执行,而不是输入停止后1000ms仅执行一次。
修复方案
方案1:用useRef保留防抖实例(推荐,可规避大部分闭包问题)
const [value, setValue] = useState<string>(""); // useRef保证整个组件生命周期内只有一个防抖实例 const handleQuery = useRef( debounce((query: string) => { console.log(query); // 此处编写搜索请求逻辑 }, 1000) ).current; useEffect(() => { handleQuery(value); // 组件卸载时清理定时器,避免内存泄漏与报错 return () => { handleQuery.cancel(); }; }, [value, handleQuery]); return ( <SearchWrapper> <SearchBar value={value} onInput={setValue} /> </SearchWrapper> );
方案2:用useCallback缓存防抖函数
给useCallback传入空依赖数组,保证防抖函数仅初始化一次:
const [value, setValue] = useState<string>(""); const handleQuery = useCallback( debounce((query: string) => { console.log(query); }, 1000), [] ); useEffect(() => { handleQuery(value); return () => handleQuery.cancel(); }, [value, handleQuery]);
注意事项
- 不要直接在防抖回调内引用外部的
value状态,避免闭包陷阱拿到过期的旧值,建议将需要用到的参数直接传入防抖函数,如示例中把当前value作为参数传递给handleQuery。 - 必须在
useEffect的清理函数中调用防抖实例的cancel方法,避免组件卸载后定时器还执行引发的逻辑错误。
内容的提问来源于stack exchange,提问作者Johnny Kontrolletti
相关产品推荐
相关产品推荐

