You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionuseInterval(callback,delay){constsavedCallback=useRef();// Remember the latest callback.useEffect(()=>{savedCallback.current=callback;},[callback]);// Set up the interval.useEffect(()=>{functiontick(){savedCallback.current();}if(delay!==null){letid=setInterval(tick,delay);return()=>clearInterval(id);}},[delay]);}
functioncancelAnimationFrameIsNotDefined(t: any): t is NodeJS.Timer{returntypeofcancelAnimationFrame===typeofundefined;}constclearRafInterval=function(handle: Handle){if(cancelAnimationFrameIsNotDefined(handle.id)){returnclearInterval(handle.id);}cancelAnimationFrame(handle.id);};
问题
前端同学们依然会有很多时候会碰到需要使用定时器的场景,比如轮播动画、轮询数据等。很多时候大家都是随手写一个
setInterval
来完成需求。比如下面的一个典型的计时器场景:
眼见的小伙伴可能已经发现了几个问题:
clearInterval
count
会有问题上面两个问题很好解决,我们稍微修改一下代码即可:
useInterval
看似我们两行代码就把问题解决了,然而上面的代码其实依然存在一些隐患,很容易导致后续更新代码的时候出现问题:
setInterval
中永远执行的是第一次render
时的匿名函数,没有更新),如果在setInterval
里面取外面的值,比如props
,那么依然无法取到最新的值setInterval
的参数不是“响应式的”,比如把1000
改成一个变量,那么要记得在useEffect
中加一下依赖关于
setInterval
错误用法会导致的问题,以及推荐的正确用法,Dan有一篇较早的博客详细进行了探讨 https://overreacted.io/making-setinterval-declarative-with-react-hooks/这篇文章中写了一个
useInterval
,完整实现如下:useInterval
的实现非常简单,19行代码中解决了几个关键问题:useRef
保证引用最新的callback
,完美解决了闭包带来的问题clearInterval
delay
变化当然我们现在不用复制这段代码,可以直接用
ahooks
提供的useInterval
即可 https://ahooks.js.org/zh-CN/hooks/use-interval用 useInterval改造一下我们的代码:
代码看起来干净整洁多了。
解决性能问题
最近实现一个通过轮询更新数据的功能时发现一个问题,这个系统是一个开发工具,几乎一直打开着,但是很多时候会切换到别的页面,在页面不显示的时候,其实没有必要更新,那么用
useInterval
就造成了很大性能浪费。按这个思路,我用
requestAnimationFrame
模拟实现了setInterval
,在浏览器每一次执行绘制任务前触发,如果页面没有显示在前台,那么就不会执行回调函数,完美解决了后台不需要刷新的问题。这里我打算写一个
useRafInterval
,在绝大多数场景下可以直接搜索替换掉useInterval
。为了实现useRafInterval
,首先需要实现一个setRafInterval
和cancelRafInterval
,用以替代setInterval
和clearInterval
实现 setRafInterval 和 cancelRafInterval
setRafInterval
基本思路是在requestAnimationFrame
中判断时间进行循环调用,代码比较简单直接上代码:需要注意两个地方:
SSR
渲染时,需要降级到setInterval
requestAnimationFrame
,所以id是会变化的,这样就没法直接返回id,需要用一个对象包装一下,目前没有找到更优雅的方式 =。=cancelRafInterval
代码更简单一些:这里逻辑上很简单,需要注意的是对
id
类型的判断,由于typeof cancelAnimationFrame === typeof undefined
不是对 handle本身的类型判断,TS无法根据这个条件推断出handle.id
的类型,需要增加一个类型守卫才可以。在
React
模块外部,其实可以直接用上面的setRafInterval
和cancelRafInterval
替换掉setInterval
和clearInterval
;实现 useRafInterval
有了
setRafInterval
和cancelRafInterval
之后,可以着手来实现useRequestInterval
了。这里我直接借鉴了ahooks
中useInterval
的实现,做了一个简单的替换即可:想用的小伙伴不用复制上面的代码了,
useRafInterval
已经在 ahooks中发布了,详细的使用文档可以参考Ahooks官方文档: https://ahooks.js.org/zh-CN/hooks/use-raf-interval另外发现提交了这个之后,不知什么时候又多了
useRafTimeout
和useRafState
,形成了一个小小的 raf家族The text was updated successfully, but these errors were encountered: