React中async/await结合debounce函数的用法及代码问题排查
问题分析与修复
核心问题1:防抖函数的apply调用错误
你的debounce函数里,setTimeout内的fn.apply(this, ...args)写法有误。apply的第二个参数要求是数组,但...args会把传入的数组参数展开成单个值,导致apply接收的不是合法参数,最终函数执行失败,API请求自然无法发起。
正确写法应该是:
timeoutId = setTimeout(() => fn.apply(this, args), delay); // 或者更简洁的箭头函数写法: timeoutId = setTimeout(() => fn(...args), delay);
核心问题2:用useMemo缓存防抖函数不合适
useMemo的定位是缓存计算结果值,虽然这里也能得到防抖函数,但语义完全不对——React专门提供了useCallback来缓存函数,用它才是正确的姿势。
额外问题:React合成事件回收导致取值失败
React的合成事件对象会被内部复用,当防抖延迟触发时,原事件对象已经被回收,evt.target.value会无法获取到正确的输入值。所以要在onChange回调里先把输入值提取出来,再传给防抖函数。
修复后的完整代码
const { useState, useCallback } = React; function debounce(fn, delay) { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn(...args), delay); } } const getApi = (terms) => `https://www.reddit.com/search.json?q=${terms}`; const Search = () => { const DELAY = 500; const [text, setText] = useState(""); const [list, setList] = useState([]); // 用useCallback缓存防抖函数 const fetchData = useCallback(debounce(async(terms) => { if (!terms) { setList([]); return; } const res = await fetch(getApi(terms)); const {data} = await res.json(); setList(data.children.map(d => d.data.title)); }, DELAY), []); return ( <div> <form className="bg-gray-200 p-5"> <input type="text" name="text" placeholder="search users..." value={text} onChange={(e) => { const value = e.target.value; setText(value); fetchData(value); // 传入提取好的value,而非事件对象 }} className="bg-white p-2 w-3/4 outline-none" /> </form> <ul> {list.map((title, index) => <li key={index}>{title}</li>)} </ul> </div> ); }; ReactDOM.render(<Search />, document.getElementById("app"));
额外补充了两点优化:
- 增加空值判断,避免输入为空时发起无效API请求
- 给列表项添加
key属性,符合React列表渲染的规范
内容的提问来源于stack exchange,提问作者Chuang Fu
相关产品推荐
相关产品推荐

