在Debounce防抖函数中无法获取自定义Hook数据的原因是什么
import { debounce } from 'lodash'; import { useState, useCallback } from 'react'; const App = () => { const [text, setText] = useTestState(); const handleSearchKeyword = (keyword: string) => { setText(keyword); sendQuery(); }; const sendQuery = useCallback( debounce(() => { console.log(text); }, 1000), [] ); return <input type="text" onChange={handleSearchKeyword} />; }; const useTestState = () => { const [text, setText] = useState(''); return [text, setText]; };
根本原因
这个问题本质是React的闭包陷阱,和自定义Hook没有关系,核心问题出在useCallback和debounce的组合使用上:
- 你给
useCallback传了空的依赖数组,这意味着sendQuery函数只会在组件首次渲染时创建1次,后续组件无论怎么重渲染,sendQuery都永远是首次渲染生成的那个版本 - 首次渲染时,
text的初始值是空字符串(你观测到的null是初始值配置差异),防抖回调捕获的永远是首次渲染上下文中的text变量,后续text更新之后,旧闭包里的text还是原来的初始值,所以打印出来不符合预期。
可行的解决方案
方案1:给useCallback补充text依赖
每次text更新时重新生成防抖函数,就能拿到最新的状态值,这个方案适配搜索防抖的场景,连续输入时防抖会自动重置,符合预期:
const sendQuery = useCallback( debounce(() => { console.log(text); }, 1000), [text] );
方案2:用useRef存储最新状态
用ref保存状态的最新引用,不受闭包限制,不需要修改useCallback的依赖:
const App = () => { const [text, setText] = useTestState(); // 用ref同步最新的text值 const textRef = useRef(text); useEffect(() => { textRef.current = text; }, [text]) const handleSearchKeyword = (keyword: string) => { setText(keyword); sendQuery(); }; const sendQuery = useCallback( debounce(() => { console.log(textRef.current); }, 1000), [] ); return <input type="text" onChange={handleSearchKeyword} />; };
方案3(最推荐):直接传参给防抖函数
不需要在防抖回调中读取状态,直接把最新的关键词作为参数传入,完全规避闭包问题,性能最优:
const handleSearchKeyword = (keyword: string) => { setText(keyword); sendQuery(keyword); }; const sendQuery = useCallback( debounce((latestKeyword) => { console.log(latestKeyword); }, 1000), [] );
内容的提问来源于stack exchange,提问作者jayjay
相关产品推荐
相关产品推荐

