React输入框添加Debounce后输入多字符触发多次API调用问题咨询
问题产生的根本原因
- 你实现的
holdSearch防抖依赖内部的定时器ID变量clear做重置逻辑,而React函数组件每次state更新都会重新执行整个函数体。setInputValue会触发inputValuestate更新,进而触发组件重渲染,每次重渲染都会重新执行const makeSearch = holdSearch(getSearch, 3000);生成全新的防抖函数实例。 - 每个防抖实例都持有独立的闭包作用域,内部的
clear变量互不关联,新的实例调用时只能清除自己作用域下的定时器,无法清除之前几次渲染生成的定时器,所以输入N个字符就会遗留N个独立定时器,倒计时结束后全部触发API调用,就出现了输入4次触发4次请求的问题。
删除setInputValue后问题解决的原理
- 删除
setInputValue后,handleChange触发时不会更新state,组件不会重渲染,全程只会在组件首次挂载时生成一次makeSearch防抖实例。所有输入触发的makeSearch调用都共享同一个闭包下的clear变量,每次新输入都会正确清除上一次的定时器,最终只会保留最后一次输入生成的定时器,3000ms后只触发1次请求,符合防抖预期。 - 注意:删除这行后你的输入框会变成非受控组件,输入内容不会同步到
inputValuestate,如果你后续需要用到输入框的state值,不能用这种解决方式。
推荐的正确实现方案
如果要保留受控组件特性,只要保证防抖函数实例不会随重渲染重新生成即可,最简单的方案是用useRef存储防抖实例:
const [inputValue, setInputValue] = useState(""); // useRef存储的内容不会随组件重渲染重置 const makeSearch = useRef(holdSearch(getSearch, 3000)).current; const handleChange = ({target}) => { setInputValue(target.value); makeSearch(target.value); }
也可以直接把定时器ID存在useRef中,写法更简洁:
const [inputValue, setInputValue] = useState(""); const timerRef = useRef(null); const handleChange = ({target}) => { setInputValue(target.value); // 每次输入都清掉上一次的定时器 clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { getSearch(target.value); }, 3000); }
内容的提问来源于stack exchange,提问作者ima.technophyle
相关产品推荐
相关产品推荐

