最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 防抖和节流及对应的React Hooks封装

    正文概述 掘金(AteaYang)   2021-02-22   642

    Debounce

    防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在定时器期间事件再次触发的话则清除重置定时器,直到定时器到时仍不被清除,事件才真正发生。

    const debounce = (fun, delay) => {
      let timer;
      return (...params) => {
        if (timer) {
          clearTimeout(timer);
        }
        timer = setTimeout(() => {
          fun(...params);
        }, delay);
      };
    };
    

    如果事件发生使一个变量频繁变化,那么使用debounce可以降低修改次数。通过传入修改函数,获得一个新的修改函数来使用。

    如果是class组件,新函数可以挂载到组件this上,但是函数式组件局部变量每次render都会创建,debounce失去作用,这时需要通过useRef来保存成员函数(下文throttle通过useRef保存函数),是不够便捷的,就有了将debounce做成一个hook的必要。

    function useDebounceHook(value, delay) {
      const [debounceValue, setDebounceValue] = useState(value);
      useEffect(() => {
        let timer = setTimeout(() => setDebounceValue(value), delay);
        return () => clearTimeout(timer);
      }, [value, delay]);
      return debounceValue;
    }
    

    在函数式组件中,可以将目标变量通过useDebounceHook转化一次,只有在满足delay的延迟之后,才会触发,在delay期间的触发都会重置计时。

    配合useEffect,在debounce value改变之后才会做出一些动作。下面的text这个state频繁变化,但是依赖的是debounceText,所以引发的useEffect回调函数却是在指定延迟之后才会触发。

    const [text,setText]=useState('');
    const debounceText = useDebounceHook(text, 2000);
    useEffect(() => {
      // ...
      console.info("change", debounceText);
    }, [debounceText]);
    
    function onChange(evt){
      setText(evt.target.value)
    }
    

    上面一个搜索框,输入完成1秒(指定延迟)后才触发搜索请求,已经达到了防抖的目的。


    Throttle

    节流函数相对于防抖函数用在事件触发更为频繁的场景上,滑动事件,滚动事件,动画上。

    看一下一个常规的节流函数 (ES6):

    function throttleES6(fn, duration) {
      let flag = true;
      let funtimer;
      return function () {
        if (flag) {
          flag = false;
          setTimeout(() => {
            flag = true;
          }, duration);
          fn(...arguments);
          // fn.call(this, ...arguments);
          // fn.apply(this, arguments); // 运行时这里的 this 为 App组件,函数在 App Component 中运行
        } else {
          clearTimeout(funtimer);
          funtimer = setTimeout(() => {
            fn.apply(this, arguments);
          }, duration);
        }
      };
    }
    

    (使用...arguments和 call 方法调用展开参数及apply 传入argument的效果是一样的)

    如何将节流函数也做成一个自定义Hooks呢?上面的防抖的Hook其实是对一个变量进行防抖的,从一个不间断频繁变化的变量得到一个按照规则(停止变化delay时间后)才能变化的变量。我们对一个变量的变化进行节流控制,也就是从一个不间断频繁变化的变量指定duration期间只能变化一次(结束后也会变化)的变量

    throttle对应的Hook实现:

    (标志能否调用值变化的函数的flag变量在常规函数中通过闭包环境来保存,在Hook中通过useRef保存)

    function useThrottleValue(value, duration) {
      const [throttleValue, setThrottleValue] = useState(value);
      let Local = useRef({ flag: true }).current;
      useEffect(() => {
        let timer;
        if (Local.flag) {
          Local.flag = false;
          setThrottleValue(value);
          setTimeout(() => (Local.flag = true), duration);
        } else {
          timer = setTimeout(() => setThrottleValue(value), duration);
        }
        return () => clearTimeout(timer);
      }, [value, duration, Local]);
      return throttleValue;
    }
    

    对应的在手势滑动中的使用:

    export default function App() {
      const [yvalue, setYValue] = useState(0);
    
      const throttleValue = useThrottleValue(yvalue, 1000);
    
      useEffect(() => {
        console.info("change", throttleValue);
      }, [throttleValue]);
    
      function onMoving(event, tag) {
        const touchY = event.touches[0].pageY;
        setYValue(touchY);
      }
      return (
        <div
          onTouchMove={onMoving}
          style={{ width: 200, height: 200, backgroundColor: "#a00" }}
        />
      );
    }
    

    这样以来,手势的yvalue值一直变化,但是因为使用的是throttleValue,引发的useEffect回调函数已经符合规则被节流,每秒只能执行一次,停止变化一秒后最后执行一次。

    对值还是对函数控制

    上面的Hooks封装其实对值进行控制的,第一个防抖的例子中,输入的text跟随输入的内容不断的更新state,但是因为useEffect是依赖的防抖之后的值,这个useEffect的执行是符合防抖之后的规则的。

    可以将这个防抖规则提前吗? 提前到更新state就是符合防抖规则的,也就是只有指定延迟之后才能将新的value进行setState,当然是可行的。但是这里搜索框的例子并不好,对值变化之后发起的请求可以进行节流,但是因为搜索框需要实时呈现输入的内容,就需要实时的text值。

    对手势触摸,滑动进行节流的例子就比较好了,可以通过设置duration来控制频率,给手势值的setState降频,每秒只能setState一次:

    export default function App() {
      const [yvalue, setYValue] = useState(0);
      const Local = useRef({ newMoving: throttleFun(setYValue, 1000) }).current;
      
      useEffect(() => {
        console.info("change", yvalue);
      }, [yvalue]);
    
      function onMoving(event, tag) {
        const touchY = event.touches[0].pageY;
        Local.newMoving(touchY);
      }
      return (
        <div
          onTouchMove={onMoving}
          style={{ width: 200, height: 200, backgroundColor: "#a00" }}
        />
      );
    }
    
    //常规节流函数
    function throttleFun(fn, duration) {
      let flag = true;
      let funtimer;
      return function () {
        if (flag) {
          flag = false;
          setTimeout(() => (flag = true), duration);
          fn(...arguments);
        } else {
          clearTimeout(funtimer);
          funtimer = setTimeout(() => fn.apply(this, arguments), duration);
        }
      };
    }
    
    

    这里就是对函数进行控制了,控制函数setYValue的频率,将setYValue函数传入节流函数,得到一个新函数,手势事件中使用新函数,那么setYValue的调用就符合了节流规则。如果这里依然是对手势值节流的话,其实会有很多的不必要的setYValue执行,这里对setYValue函数进行节流控制显然更好。


    起源地下载网 » 防抖和节流及对应的React Hooks封装

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元