节流与防抖及react hook版本的实现方式

926 阅读1分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

节流

指触发事件后在规定时间内函数只能执行一次,如果在时间范围内再次触发,则会执行最后一次,稀释事件触发频率。达到前端展示及功能的优化。

在开发过程常见场景

scroll,浏览器播放事件等
看下防抖的实现
import { useEffect, useCallback, useRef } from 'react';

function useThrottle(fn, delay, dep = []) {
  const { current } = useRef({ fn, timer: null })
  useEffect(function () {
     current.fn = fn;
  }, [fn]);

  return useCallback(function f(...args) {
    if (!current.timer) {
      current.timer = setTimeout(() => {
        delete current.timer
      }, delay)
      current.fn.call(this, ...args)
    }
  }, dep)
}

export default useThrottle;

防抖

指触发事件后在规定时间内函数,如果在时间不能被重复触发范围内再次触发,则会重新计算函数执行时间。 如果有人点击电梯按钮,就会多等待5秒,此时如果又有人点击,那么电梯就会再多等待 10 秒。

在开发过程常见场景

window resize,鼠标划入划出、keyup、keydown,搜索及表单提交等
实现的时候需要注意一些小问题
1.  增加取消功能
2.  增加立即执行功能
3.  `useCallback` 函数的第二个参数,做依赖更新。
4.  `useRef` 永久存贮,借助 `useEffect` 更新永久存贮。
看下防抖的实现
import React, { useState, useCallback, useEffect, useRef } from 'react';

const rhDebu: React.FC<any> = () => { 
    const [count, setCount] = useState<number>(0); 
    const handleClick = useCallback(data => { console.log(data, 'data'); }, []); 
    const func = React.useCallback(() => { setCount(count => count + 1); return 2; }, []); 
    const handleAddCount = UseDebounce(func, 3000, true, handleClick); 
    
    return ( 
        <div> 
            <button onClick={handleAddCount}>count|{count}</button> 
        </div> 
    ); 
};
export default rhDebu; 

function UseDebounce( func: any, wait: number, immediate: boolean, callback?: any, ): any {
let timer = useRef<NodeJS.Timeout | null>(); 
const fnRef = useRef<any>(func); useEffect(() => { fnRef.current = func; }, [func]); 
const timerCancel = function() { if (timer.current) clearTimeout(timer.current); }; 

function debounced(...args: any[]) { 
    const runFunction = () => { 
        return callback 
            ? callback(fnRef.current.apply(fnRef.current, args)) 
            : fnRef.current.apply(fnRef.current, args);
    }; 
    timerCancel(); 
    if (immediate) { 
        let runNow = !timer.current; 
        timer.current = setTimeout(() => { timer.current = null; }, wait);
        if (runNow) { 
            runFunction(); 
        }
    } else { 
        timer.current = setTimeout(() => { runFunction(); }, wait); } 
    } 
    debounced.cancel = function() { timerCancel(); timer.current = null; };
    return useCallback(debounced, [wait, immediate, timerCancel, func]); 
}