useRafInterval
作用
一个可以处理 setInterval 的 Hook, 用于代替 setInterval。
原理
用
requestAnimationFrame
模拟实现setInterval
,API
和useInterval
保持一致,好处是可以在页面不渲染的时候停止执行定时器,比如页面隐藏或最小化等.请注意,如下两种情况下很可能是不适用的,优先考虑
useInterval
:
- 时间间隔小于 16ms
- 希望页面不渲染的情况下依然执行定时器
源码
ts
import { useCallback, useEffect, useRef } from 'react'
import useLatest from '../useLatest'
import { isNumber } from '../utils'
interface Handle {
id: number | NodeJS.Timer
}
// 用 requestAnimationFrame 模拟实现 setInterval
const setRafInterval = function (callback: () => void, delay: number = 0): Handle {
// 如果不支持 requestAnimationFrame,则使用 setInterval
if (typeof requestAnimationFrame === typeof undefined) {
return {
id: setInterval(callback, delay)
}
}
let start = new Date().getTime()
const handle: Handle = {
id: 0
}
// 调用 requestAnimationFrame生成动画
const loop = () => {
const current = new Date().getTime()
if (current - start >= delay) {
callback()
start = new Date().getTime()
}
handle.id = requestAnimationFrame(loop)
}
handle.id = requestAnimationFrame(loop)
return handle
}
// 判断是否支持cancelAnimationFrame
function cancelAnimationFrameIsNotDefined(t: any): t is NodeJS.Timer {
return typeof cancelAnimationFrame === typeof undefined
}
const clearRafInterval = function (handle: Handle) {
// 不支持cancelAnimationFrame就用clearInterval
if (cancelAnimationFrameIsNotDefined(handle.id)) {
return clearInterval(handle.id)
}
// 支持就用cancelAnimationFrame清除动画
cancelAnimationFrame(handle.id)
}
function useRafInterval(
fn: () => void,
delay: number | undefined,
options?: {
immediate?: boolean
}
) {
const immediate = options?.immediate
const fnRef = useLatest(fn)
const timerRef = useRef<Handle>()
useEffect(() => {
if (!isNumber(delay) || delay < 0) return
if (immediate) {
fnRef.current()
}
// 使用setRafInterval来执行回调
timerRef.current = setRafInterval(() => {
fnRef.current()
}, delay)
return () => {
if (timerRef.current) {
// 清除定时器
clearRafInterval(timerRef.current)
}
}
}, [delay])
// 返回清除定时器的函数
const clear = useCallback(() => {
if (timerRef.current) {
clearRafInterval(timerRef.current)
}
}, [])
return clear
}
export default useRafInterval