react hooks中使用防抖节流要注意的问题

85 阅读2分钟

前言

在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>
        </>
    )
}