防抖(debounce)和节流(throttle)是前端经常用到的工具函数。
在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。
通常情况下,我们习惯于使用lodash提供的工具函数,那么如何自己封装防抖节流的hooks?
首先了解下概念:
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
function debounce(fn, ms) { let timer; return function(...args) { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn(...args) timer = null; }, ms); } } 当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
function throttle(fn, ms) { let timer; return function(...args) { if (timer) return; canRun = false; timer = setTimeout(() => { fn(...args); timer = null; }, ms); } } 防抖函数必须在只执行一次的位置调用。在类组件中,放在constructor里或者变量函数生成的时候都可以,因为类组件只会初始化一次,后续组件中绑定的函数永远是不变的,因此依据闭包原理保存下来的状态会起作用。
而在函数式组件中,每次render时,内部函数会重新生成并绑定到组件上去。当组件只有一个state会影响render时,我们
但是当有其他state影响渲染后
count2发生变化,重新渲染counter1发生变化流程的对比就是这样了,现在你明白为什么正常的防抖函数不能用在 reack hook 里了么?
那么,怎么实现react hook防抖呢?核心思想就是,保证每次渲染时,绑定到组件上的函数是同一个防抖函数。
防抖hook
import { useCallback, useEffect, useRef } from 'react'; export interface UseRefParams { fn: (_args: any) => void; timer: ReturnType | null; } // React anti shake function export const useDebounce = (fn: (_args: any) => void, delay = 2000) => { const { current } = useRef({ fn, timer: null }); useEffect(() => { current.fn = fn; }, [current, fn]); return useCallback( (args: any) => { if (current.timer) { clearTimeout(current.timer); } current.timer = setTimeout(() => { current.fn(args); }, delay); }, [current, delay] ); }; 在react当中节流注意细节与防抖一样,这里不做陈述,直接上代码:
import { useCallback, useEffect, useRef } from 'react'; export interface UseRefParams { fn: (_args: any) => void; timer: ReturnType | null; } // React throttling function export const useThrottle = (fn: (_args: any) => void, delay = 2000) => { const { current } = useRef({ fn, timer: null }); useEffect( function () { current.fn = fn; }, [current, fn] ); return useCallback( function f(args: any) { if (!current.timer) { current.timer = setTimeout(() => { current.timer = null; }, delay); current.fn(args); } }, [current, delay] ); };