First of all, setInterval is Hook encapsulated 👇
import { useEffect, useRef } from 'react'
/** * interTerval hooks component *@param Fn executes the function *@param Delay time *@param Options immediate If the value is true, the fn function is executed immediately and then the timer */ is executed
function useInterval(
fn: () => void,
delay: number | null | undefined, options? : { immediate? :boolean
}
) :void {
constimmediate = options? .immediateconst timerRef = useRef<() = > void>()
timerRef.current = fn
useEffect(() = > {
if (delay === undefined || delay === null) {
return
}
if(immediate) { timerRef.current? (1)}.const timer = setInterval(() = >{ timerRef.current? .() }, delay)return () = > {
clearInterval(timer)
}
}, [delay])
}
export default useInterval
Copy the code
Implement countdown Hook
import { useState, useEffect, useRef, useMemo } from 'react'
import { useInterval } from '/'
interface ITime {
/** The current time */currentTime? :number
/** End time */endTime? :number
/** Another way to pass the current time and end time, directly pass the time difference */differTime? :number
}
interface ICbTime {
d: number
h: number
m: number
s: number
}
/** ** **@param Options Time object *@param Cb the callback function executed when the countdown is complete@param NoImmediate Specifies whether to execute the callback immediately when the callback conditions are met. The default value is false
function useCountDown(options: ITime, cb? : () = >void, noImmediate? :boolean
) :ICbTime {
const { currentTime = 0, endTime = 0, differTime = 0 } = options
const [diffTime, setDiffTime] = useState(0)
/** The time when the component receives the parameter */
const entryTime = useRef<number> (0)
/** The time difference required for the current countdown */
const maxTime = useRef<number> (0)
/** Whether a callback can be performed */
const isImplementCb = useRef(false)
useEffect(() = > {
if(! isImplementCb.current) { isImplementCb.current =true
}
if ((currentTime > 0 && endTime > 0) || differTime > 0) {
entryTime.current = new Date().getTime()
maxTime.current = differTime > 0 ? differTime : endTime - currentTime
if (maxTime.current <= 0 && noImmediate) {
isImplementCb.current = false
}
setDiffTime(maxTime.current)
}
}, [currentTime, endTime, differTime])
useInterval(
() = > {
const curtTimes = new Date().getTime()
const TimeDifference = curtTimes - entryTime.current
setDiffTime(maxTime.current - TimeDifference)
},
diffTime <= 0 ? null : 1000
)
const timeObj = useMemo(() = > {
const time = diffTime > 0 ? diffTime / 1000 : 0
const d = Math.floor(time / (24 * 60 * 60))
const h = Math.floor((time / (60 * 60)) % 24)
const m = Math.floor((time / 60) % 60)
const s = Math.ceil(time % 60)
if (diffTime <= 0 && isImplementCb.current) {
/** * setTimeout: * annot update during an existing state transition (such as within `render`). * Render methods should be a pure function of props and state. */
setTimeout(() = >{ cb? . ()},0)}return { d, h, m, s }
}, [diffTime])
return timeObj || ({} as ICbTime)
}
export default useCountDown
Copy the code
Write a demo to see the effect 👇
const TimeArea = () = > {
const { d, h, m, s } = useCountDown(
{
currentTime: 1631262176333.endTime: 1831062176333
},
() = > {
alert('Countdown is over')})return (
<div style={{ width: '200px', height: '200px' }}>{d} days to the end of the mission<i>{h < 10 ? '0' + h : h}</i>:
<i>{m < 10 ? '0' + m : m}</i>:<i>{s < 10 ? '0' + s : s}</i>
</div>)}Copy the code