首页 > 其他分享 >React-Hooks怎样封装防抖和节流-面试真题

React-Hooks怎样封装防抖和节流-面试真题

时间:2022-10-17 08:34:46浏览次数:89  
标签:function 防抖 函数 真题 Hooks flag duration 节流

Debounce

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

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的效果是一样的)

扩展:在ES6之前,没有箭头函数,需要手动保留闭包函数中的this和参数再传入定时器中的函数调用:

所以,常见的ES5版本的节流函数:

function throttleES5(fn, duration) {
  let flag = true;
  let funtimer;
  return function () {
    let context = this,
      args = arguments;
    if (flag) {
      flag = false;
      setTimeout(function () {
        flag = true;
      }, duration);
      fn.apply(context, args); // 暂存上一级函数的 this 和 arguments
    } else {
      clearTimeout(funtimer);
      funtimer = setTimeout(function () {
        fn.apply(context, args);
      }, duration);
    }
  };
}

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

throttle对应的Hook实现:

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

参考:前端react面试题详细解答

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函数进行节流控制显然更好。

需要注意的是,得到的新函数需要通过useRef作为“实例变量”暂存,否则会因为函数组件每次render执行重新创建。

标签:function,防抖,函数,真题,Hooks,flag,duration,节流
From: https://www.cnblogs.com/beifeng1996/p/16797848.html

相关文章

  • Ant design——message防抖优化——富文本的使用——富文本数据的收集——上传图片保
    button按钮有个loading加载态度。加载状态为true则不可点击。通过这个属性来完成项目中的防抖优化const[seachBtn,setSeachBtn]=useState(false)message.error("用户名不......
  • Hooks与普通函数的区别
    Hooks与普通函数的区别在这里的Hooks具体指的是自定义Hooks,自定义的Hooks与我们定义的普通函数类似,都可以封装逻辑,以实现逻辑的复用。Hooks实际上是一种特殊的函数,而由于H......
  • vue-hooks__钩子函数__进阶
    vue-hooks__钩子函数__进阶联合其他模块进行使用useHandleData用于处理数据import{ElMessageBox,ElMessage}from"element-plus";//引入element-plus的弹窗组......
  • vue-hooks__钩子函数
    vue-hooks__钩子函数1.什么是钩子函数钩子函数是一种在某个时刻被调用的函数,它可以让我们在某个时刻做一些事情,比如在组件挂载之前做一些事情,或者在组件更新之前做一些......
  • vite vue3 规范化与Git Hooks
    在《JS模块化》系列开篇中,曾提到前端技术的发展不断融入很多后端思想,形成前端的“四个现代化”:工程化、模块化、规范化、流程化。在该系列文章中已详细介绍了模块化的发......
  • 【pytest官方文档】解读- 插件开发之hooks 函数(钩子)
    上一节讲到如何安装和使用第三方插件,用法很简单。接下来解读下如何自己开发pytest插件。但是,由于一个插件包含一个或多个钩子函数开发而来,所以在具体开发插件之前还需要先......
  • Umi 配置 @umijs/fabric(ESLint+Prettier+Stylelint) + gitHooks + VSCode
    Umi配置@umijs/fabric(ESLint+Prettier+Stylelint)+gitHooks+VSCode顶尖金字教程wywppkd2022年04月23日16:45 ·  阅读745@umijs/fabric 是Umi官......
  • 函数节流与防抖
    参考链接:https://www.cnblogs.com/xiaoyantongxue/p/15616352.html函数节流:英文 throttle 有节流阀的意思。大致意思也是节约触发的频率那么,函数节流,真正的含义是:单......
  • React-Hooks怎样封装防抖和节流-面试真题
    Debouncedebounce原意消除抖动,对于事件触发频繁的场景,只有最后由程序控制的事件是有效的。防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在......
  • 最全的2021蓝桥杯算法课《算法很美》的学习笔记总目录+真题详解
    这里写目录标题​​第一章位运算​​​​第二章递归​​​​第三章查找与排序​​​​......