React 中class与hook渲染机制方面的一点思考

刷掘金时无意看到了一篇文章:React hooks 怎样做防抖?
点进去看了一遍么,才发现之前对于hooks仅仅只是会用,对于hook的渲染机制并不十分的了解。
我们先从上篇博客提到防抖来入手,再谈渲染机制。

我们平时是如何写防抖的?

function debounce(fn,delay) {
    let timer;
    return function (...arg) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(()=>{
            fn(arg)
            clearTimeout(timer)
        },delay)
    }
}

这是一个很普通的防抖函数,他的原理很简单,利用闭包做一个缓存的变量即可实现

防抖:如果在规定时间内做点击,在设定时间内若上一次还未执行,则重新及时,到达时间后执行(可以理解为法师施法,打断了就要重新施法)

那我们在hooks中是如何使用的呢?

你可能会说,我们直接上用上面那个不就ok了

function useDebounce(fn,delay){
		return debounce(fn,delay) //我直接复用上面定义的防抖代码
}

ok那我们做一个示例:

 const [counter1, setCounter1] = useState(0);
 const [counter2, setCounter2] = useState(0);

 const handleClick = useDebounce(function() {
        //console.count('click1')
        setCounter1(counter1 + 1)
    }, 500)

function debounce(fn,delay) {
    let timer;
    return function (...arg) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(()=>{
            fn(arg)
            clearTimeout(timer)
        },delay)
    }
}

function useDebounce(fn,delay){
		return debounce(fn,delay) //我直接复用上面定义的防抖代码
}

// 引入间隔计时对count2++
useEffect(function() {
        console.log("useEffect被调用")
        const t = setInterval(() => {
            setCounter2(x => x + 1)
        }, 500);
        //return的函数  会在调用一个新的 effect 之前对前一个 effect 进行清理
         return clearInterval.bind(undefined, t)  //这个清除阶段 每次都会在下次useEffect执行前执行

    }, [])
    
 return <div style={{ padding: 30 }}>
        <button
            ref={inputEl}
            onClick={function() {
                handleClick()
            }}
        >click</button>
        <div>{counter1}</div>
 		 <div>{counter2}</div> //counter2在这里
    </div>

这里引入了一个setInterval,他不停的对counter2++,目的是为了引起这个组件渲染,接下来我们看看防抖效果

在这里插入图片描述

上图是在hooks调用的,防抖函数的延时时间传入1000ms
我在1000ms内进行了多次点击,理论上他应该再等1000ms,但他却在我第一次点击1s触发了。
即防抖失效

接下来我们来想想为什么?

hooks的一个渲染机制

我们平时在class组件,调用这个防抖函数他就可以渲染,即防抖内的闭包生效,但是到了hooks中却失效了,这是因为hooks在执行渲染会将所有的hooks重新去执行一遍,这样我们之前定义的timer他就不存在被重新渲染了,可以结合Hook渲染每次渲染顺序必须一致来理解。

你可能问为什么我在class组件就能用呢?

仔细想想我们就可以想到,class组件的重新渲染是render下的JSX那块的代码,在上面定义的方法并未重新的去渲染

因此我们可以明白,这是一个渲染执行而导致的问题,接下来我们用hooks的缓存机制来解决上面的防抖。

//对之前的Debounce进行改造
function useDebounce(fn,delay) {
        const {current} = useRef({}); //ref改变时不会改变任何值
        //且 其 .current 属性中保存一个可变值的“盒子”,其中可以保存任何值 我们就可以利用这样一个缓存机制
        // 当然他也有其他缓存机制,例如useCallBack和useMemo,但是我实在想不通,如何把timer提出去,除了使用useRef

        /*
        *
        * 原因:因为在class组件,每次重新渲染的都是render下的jsx模板,
        * hook不一样,他每次都会重新渲染全部的hook,因此会导致闭包每次的失效,
        * 不然为什么官方文档中会去做规定,每次渲染的位置必须都是一致的
        * */
        function f(...args){
            if (current.timer) {
                clearTimeout(current.timer);
            }
            current.timer = setTimeout(fn.bind(undefined, ...args), delay);
        }

        return f
    }
    
 const handleClick = useDebounce(function() {
        setCounter1(counter1 + 1)
    }, 500)

我们借useRef来实习一个缓存机制,关于useRef移步官方文档

PS:这其中只是包含我个人的一个理解,如果有不太对的地方还请指正交流啦ovo

参考文章:https://juejin.im/post/5ea04691e51d4547126400c7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值