前言
在React中,函数组件和类组件的生命周期是不同的。
函数组件每次渲染都会重新创建,而类组件则会在每次渲染时复用之前创建的实例,只是更新状态和属性。
所以,对于函数组件来说,如果你在组件中定义了函数,那么每次重新渲染时这个函数都会被重新创建。而你在类组件中定义了函数,这个函数并不会在每次渲染时重新创建,因为类组件的实例是可以复用的。
不过,在react hooks中,也提供了几种方法来缓存我们创建的函数,进而让hooks记住它,避免不必要的渲染。
第一种方法:useCallback
const xxx = useCallback(callback,[dependencies])
在函数第一次创建,使用useCallback会将该函数地址缓存起来,当组件再次更新的时候,会拿到该地址,再次赋给该函数,类似于一个引用缓存的操作。
-
组件第一次渲染,useCallback执行,创建一个函数“callback”,赋值给xxx
-
组件后续每一次更新,判断依赖的状态值是否改变,如果改变,则重新创建新的函数堆,赋值给xxx;但是如果,依赖的状态没有更新「或者没有设置依赖“[]”」则xxx获取的一直是
-
第一次创建的函数堆,不会创建新的函数出来!!
-
或者说,基于useCallback,可以始终获取第一次创建函数的堆内存地址(或者说函数的引用)
第二种方法:useRef
除了 useCallback 之外,我们还可以利用 useRef 来实现函数的存储,它和 React 类组件中的实例属性相似。
useRef 是一个可修改的对象,我们可以在 useRef 的 current 属性传入最初的值,并且该值会在组件的整个生命周期中保持不变,除非你手动去修改它。
所以,使用 useRef也可以达到我们要的效果:
const xxx = useRef(callback)
有了以上两种方法,我们就可以在react hooks实现防抖和节流了,下面以按钮的防抖为例:
import React, {useCallback,useRef} from 'react';
import {debounce} from 'lodash';
export default function App(){
//使用useCallback
const doSomething = useCallback(debounce(()=>{
console.log(`do something`)
}, 1000),[])
const onClick = () => {
doSomething();
}
//使用useRef
const ref = useRef(debounce(()=>{
console.log(`do something`)
}, 1000))
const onClick = () => {
ref.current();
}
return (
<>
<Button title="防抖" onClick={onClick}></Button>
</>
)
}
自己封装防抖钩子函数
上面是使用了lodash库中的防抖方法,我们也可以自己封装一个防抖钩子函数。
function useDebounce(fn,wait){
const [timer,setTimer] = useState(null);
useEffect(()=>{
return ()=>{
if(timer){
clearTimeout(timer)
}
}
},[timer])
const debounceFn = () => {
if(timer){
clearTimeout(timer)
}
setTimer(setTimeout(()=>{
fn();
},wait))
}
return debounceFn;
}
function App(){
const onClick = useDubounce(()=>{
console.log(`do something`)
},1000)
return (
<>
<Button title="防抖" onClick={onClick}></Button>
</>
)
}