Skip to content

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

如有转载或 CV 的请标注本站原文地址