React Native 中實現倒計時功能

正文

在 React Native,該如何實現一個倒計時功能呢?

首次實現

表面看來很簡單,譬如像下面這樣:

const timer = useRef<ReturnType<typeof setInterval> | null>(null)
const [count, setCount] = useState(0)
const start = () => {
  setCount(10)
  timer.current = setInterval(() => {
    setCount((count) => count - 1)
  }, 1000)
}
useEffect(() => {
  if (count === 0 && timer.current !== null) {
    clearInterval(timer.current)
    timer.current = null
  }
}, [count])

這段代碼大多數情況下是可以正常工作的。但是你將應用退到後臺,稍後再進入看看。

很有可能,原本應該結束的倒計時,還在工作。

這是因為 React Native 應用退到後臺後,世界會停止。為瞭適應這點,我們應該先設定希望倒計時結束的時間,然後每隔一秒計算一次當前時間與結束時間之差(秒)。

此外,當應用退到後臺時,應該清除定時器。

最終實現

考慮上述種種,倒計時的實現並不簡單。

我們可以封裝一個自定義 Hook 來提供可復用的倒計時功能。

import { useAppState } from '@react-native-community/hooks'
import { useCallback, useEffect, useRef, useState } from 'react'
export function useCountdown(seconds = 30) {
  const timer = useRef<ReturnType<typeof setInterval> | null>(null)
  const [target, setTarget] = useState<Date | null>(null)
  const [count, setCount] = useState<number>(0)
  const appState = useAppState()
  const start = useCallback(() => {
    setTarget(add(new Date(), seconds))
  }, [seconds])
  const stop = useCallback(() => {
    setTarget(null)
    setCount(0)
  }, [])
  useEffect(() => {
    if (target === null || appState !== 'active') {
      return
    }
    setCount(diff(new Date(), target))
    timer.current = setInterval(() => {
      setCount(diff(new Date(), target))
    }, 1000)
    return () => {
      if (timer.current) {
        clearInterval(timer.current)
        timer.current = null
      }
    }
  }, [target, appState])
  useEffect(() => {
    if (count === 0) {
      stop()
    }
  }, [count, stop])
  return { count, start, stop }
}
function add(date: Date, seconds: number) {
  return new Date(date.getTime() + seconds * 1000)
}
function diff(now: Date, target: Date) {
  return Math.max(
    Math.trunc((target.getTime() - now.getTime()) / 1000 + 0.5),
    0
  )
}

示例

這裡有一個示例,供你參考。

以上就是React Native 中實現倒計時功能的詳細內容,更多關於React Native倒計時的資料請關註WalkonNet其它相關文章!

推薦閱讀: