hook,什么,防抖函数?

19 篇文章 2 订阅
10 篇文章 2 订阅

防抖(debounce)是前端经常用到的一个工具函数,也是在面试中常常被问到的一个问题。

防抖函数

经典的防抖函数:

function debounce(fn, ms) {
  let timer;
  return function(...args) {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn(...args)
      timer = null;
    }, ms);
  }
}

react hooks版的防抖函数

export default function() {
  const [counter, setCounter] = useState(0);

  const handleClick = useDebounce(function() {
    setCounter(counter + 1)
  }, 1000)

  return <div style={{ padding: 30 }}>
    <Button
      onClick={handleClick}
    >click</Button>
    <div>{counter}</div>
  </div>
}

实际使用的时候,是这样的

export default function() {
  const [counter1, setCounter1] = useState(0);
  const [counter2, setCounter2] = useState(0);

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

  useEffect(function() {
    const t = setInterval(() => {
      setCounter2(x => x + 1)
    }, 500);
    return clearInterval.bind(undefined, t)
  }, [])


  return <div style={{ padding: 30 }}>
    <Button
      onClick={function() {
        handleClick()
      }}
    >click</Button>
    <div>{counter1}</div>
    <div>{counter2}</div>
  </div>
}

当引入一个自动累加counter2就开始出问题了。这时很多候选人就开始懵了,有的候选人会尝试分析原因。只有深刻理解react hooks在重渲染时的工作原理才能快速定位到问题(事实上出错不要紧,能够快速定位问题的小伙伴才是我们苦苦寻找的)。

你可能会这样修改

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

还可能这样修改

function useDebounce(fn, delay) {
  return useCallback(debounce(fn, delay), [])
}

在配合setCounter1(x => x + 1)修改的情况下,可以得到正确的结果。

但显然你仔细测试一下,还是没有真正的解决问题。

那么,问题在哪里呢?

function useDebounce(fn, time) {
  console.log('usedebounce') // 打印这里看一下
  return debounce(fn, time);
}

控制台开始疯狂的输出log...,大脑在飞快的思考。

每次组件重新渲染,都会执行一遍所有的hooks,这样debounce高阶函数里面的timer就不能起到缓存的作用(每次重渲染都被置空)。timer不可靠,debounce的核心就被破坏了。

这才是优化

// 加入缓存机制
function useDebounce(fn, delay, dep = []) {
const { current } = useRef({ fn, timer: null });
  useEffect(function () {
    current.fn = fn;
  }, [fn]);
return useCallback(function f(...args) {
if (current.timer) {
      clearTimeout(current.timer);
    }
    current.timer = setTimeout(() => {
      current.fn.call(this, ...args);
    }, delay);
  }, dep)
}
// 
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);
}

react hooks可以帮助我们把一些常用的状态逻辑沉淀下来,你学会了吗?

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端e站

如果有所帮助,欢迎来杯奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值