React中的防抖
背景
输入框,在输入的过程中搜索匹配的内容下拉框展示。配合change
事件搜索匹配结果会导致搜索很频繁。自然而然想到使用防抖避免频繁查询。
防抖不生效
function Com(){
const [schoolValue, setSchoolValue] = useState('')
const debounced = (fn) => {
let timeout = null
return function () {
let context = this;
let args = arguments;
//注意这里--------
setSchoolValue(args[0])
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
fn.apply(context, args)
}, 1000)
}
}
const searchSchool = debounced((v) => {
console.log('进行搜索操作')
})
return <Input value={schoolValue} onChange={searchSchool} placeholder='请输入' />
}
由于输入框的value
绑定的是schoolValue
,所以需要在数据更改的时候(也就是change
的时候)需要setSchoolValue
,不然会导致输入框不能输入内容。
但是setSchoolValue
会导致组件重新render
,render
之后会导致debounced
重新声明,timeout
自然也被重新赋值。之前的timeout
没有清除,后续一并触发,所以没有达到防抖的效果。
如果按照如下,则不会打印“进行搜索操作
”:
function Com(){
const [schoolValue, setSchoolValue] = useState('')
let timeout = null
useEffect(()=>{
return ()=>{clearTimeout(timeout)} //注意这里--------清除之前的timeout
})
const debounced = (fn) => {
return function () {
let context = this;
let args = arguments;
setSchoolValue(args[0])
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
fn.apply(context, args)
}, 1000)
}
}
const searchSchool = debounced((v) => {
console.log('进行搜索操作')
})
return <Input value={schoolValue} onChange={searchSchool} placeholder='请输入' />
}
防抖生效方案1——ref
这个问题类似于函数组件的闭包陷阱,同样也可以通过ref
解决:
function Com(){
const [schoolValue, setSchoolValue] = useState('')
let timeout = useRef()
const debounced = (fn) => {
return function () {
let context = this;
let args = arguments;
setSchoolValue(args[0])
if (timeout.current) {
clearTimeout(timeout.current)
}
timeout.current = setTimeout(() => {
fn.apply(context, args)
}, 1000)
}
}
const searchSchool = debounced((v) => {
console.log('进行搜索操作')
})
return <Input value={schoolValue} onChange={searchSchool} placeholder='请输入' />
}
因为在不同的render
中ref
总是指向同一个引用地址,所有的操作都是在这一个地址上操作的。
防抖生效方案2——useCallback
function Com(){
const [schoolValue, setSchoolValue] = useState('')
const debounced = (fn) => {
let timeout = null
return function () {
let context = this;
let args = arguments;
setSchoolValue(args[0])
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
fn.apply(context, args)
}, 1000)
}
}
// useCallback缓存函数
const searchSchool = useCallback(debounced((v) => {
console.log('进行搜索操作')
}),[])
return <Input value={schoolValue} onChange={searchSchool} placeholder='请输入' />
}
useCallback部分源码
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
// 获取上一次依赖的值
const prevDeps: Array<mixed> | null = prevState[1];
// 判断 update 前后 dep 是否变化
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 未变化,则返回上一次的值
return prevState[0];
}
}
}
// 变化,将新的 callback 作为value
hook.memoizedState = [callback, nextDeps];
return callback;
}
参考链接
转载自:https://juejin.cn/post/7211315741902749752