useRafTimeout
作用
setTimeout
的升级版,使用 requestAnimationFrame
实现的 setTimeout
原理
用
requestAnimationFrame
模拟实现setTimeout
,好处是可以在页面不渲染的时候停止执行定时器,比如页面隐藏或最小化等.
源码
ts
import { useCallback, useEffect, useRef } from 'react'
import useLatest from '../useLatest'
import { isNumber } from '../utils'
interface Handle {
id: number | NodeJS.Timeout
}
const setRafTimeout = function (callback: () => void, delay: number = 0): Handle {
if (typeof requestAnimationFrame === typeof undefined) {
return {
id: setTimeout(callback, delay)
}
}
const handle: Handle = {
id: 0
}
const startTime = new Date().getTime()
const loop = () => {
const current = new Date().getTime()
if (current - startTime >= delay) {
callback()
} else {
handle.id = requestAnimationFrame(loop)
}
}
handle.id = requestAnimationFrame(loop)
return handle
}
function cancelAnimationFrameIsNotDefined(t: any): t is NodeJS.Timer {
return typeof cancelAnimationFrame === typeof undefined
}
const clearRafTimeout = function (handle: Handle) {
if (cancelAnimationFrameIsNotDefined(handle.id)) {
return clearTimeout(handle.id)
}
cancelAnimationFrame(handle.id)
}
function useRafTimeout(fn: () => void, delay: number | undefined) {
const fnRef = useLatest(fn)
const timerRef = useRef<Handle>()
useEffect(() => {
if (!isNumber(delay) || delay < 0) return
timerRef.current = setRafTimeout(() => {
fnRef.current()
}, delay)
return () => {
if (timerRef.current) {
clearRafTimeout(timerRef.current)
}
}
}, [delay])
const clear = useCallback(() => {
if (timerRef.current) {
clearRafTimeout(timerRef.current)
}
}, [])
return clear
}
export default useRafTimeout