开发技术

react , hooks , ts , taro

需求分析

需要一个可以按天,时,分和秒来进行倒计时的组件。

简单使用

注:主要逻辑请看 useCountDown

import CountDown from '@/components/countDown';

import { useEffect, useState } from 'react';

import Taro from '@tarojs/taro';

const curTime = Date.now()

export default () => {const [createTime, setCreateTime] = useState(0);useEffect(() => {setTimeout(() => {// 假设:异步获取五分钟前创建的日期setCreateTime(Date.now() - 5 * 60_000)}, 1000)}, [])return (<>{/* 倒计时10秒 */} {if(v <= 0) Taro.showToast({title: '到时间了', icon: 'none'})}} />{/* 倒计时3天 */}{/* 异步获取五分钟前创建的日期 */})

}

countDown组件的封装

props 传参很简单,只有一个 value 起始时间 , 一个 total 倒计时总时间,还有一个 onChange 返回当前的倒计时剩余时间,当为0的时候就可以进行业务需要的倒计时为0的操作了。

import useCountDown from '@/hooks/useCountDown';

import { formatRemainTime } from '@/utils/format';

import { Text } from '@tarojs/components'

import { useEffect } from 'react';

import './index.less';

type PropsType = { /** 起始时间 如果不传就是现在的时间 */value?: number/** 倒计时时间,默认40min */total?: numberonChange?: (val: number) => void

}

export default ({value, total = 2400_000, onChange}: PropsType) => {// 倒计时const [countDownNum, setCountDown] = useCountDown()useEffect(() => {if(value !== void 0) {setCountDown(value, total)}}, [value])useEffect(() => {if(countDownNum !== void 0) {onChange?.(countDownNum)}}, [countDownNum])return ({formatRemainTime(countDownNum)})

}

useCountDown 的封装

首先定义一个计时器变量 timerRef 主要是用于保存/清除计时器;定义一个用来保存一个当前时间的 state;最后再定义一个参数保存值。

const timerRef = useRef()

const [time, setTime] = useState()

const params = useRef({beginTime: 0,total: 0

})

当调用 setCountDown 函数时,开启计时器倒计时,同时需要保存一份函数的参数。

// 开启计时器的函数, beginTime: 输入的初始时间 total: 需要计算的时间

const setCountDown = (beginTime: number, total: number) => {params.current = { beginTime, total }clearInterval(timerRef.current)_setInterval(beginTime, total)

}

开启计时器,根据传入的开始时间,倒计时总时间和当前时间来计算出,当前倒计时的剩余值,如果该值大于0,则表示还有时间可以倒计时。采用 setInterval 每隔一秒触发一次 setTime ,当时间小于0清除计时器即可。

const _setInterval = (beginTime: number, total: number) => {if(!beginTime || !total) returnconst interval = beginTime + total - Date.now()if(interval < 0) returnsetTime(interval)timerRef.current = setInterval(() => {setTime(t => {const _t = t! - 1000if(_t <= 0) {clearInterval(timerRef.current)return 0}return _t})}, 1000);

}

注意一:销毁组件时,记得清除计时器防止内存泄漏

useEffect(() => {return () => {clearInterval(timerRef.current)}

}, [])

注意二:由于我这里是用在小程序端的,当小程序退到后台隔几秒后,计时器会自动被停掉,所以当小程序重新展示出来时需要重新开始一下计时器。

useDidShow(() => {clearInterval(timerRef.current)_setInterval(params.current.beginTime, params.current.total)

})

useCountDown 完整代码

import { useEffect, useRef, useState } from "react"

import { useDidShow } from "@tarojs/taro"

/** 倒计时钩子 */

const useCountDown = () => {const timerRef = useRef()const [time, setTime] = useState()const params = useRef({beginTime: 0,total: 0})// 退到后台,大约过个六七秒后定时器会自动暂停掉(回来后自动继续之前的定时器),所以会导致时间不准确useDidShow(() => {clearInterval(timerRef.current)_setInterval(params.current.beginTime, params.current.total)})useEffect(() => {return () => {clearInterval(timerRef.current)}}, [])// 开启计时器的函数, beginTime: 输入的初始时间 total: 需要计算的时间const setCountDown = (beginTime: number, total: number) => {params.current = { beginTime, total }clearInterval(timerRef.current)_setInterval(beginTime, total)}const _setInterval = (beginTime: number, total: number) => {if(!beginTime || !total) returnconst interval = beginTime + total - Date.now()if(interval < 0) returnsetTime(interval)timerRef.current = setInterval(() => {setTime(t => {const _t = t! - 1000if(_t <= 0) {clearInterval(timerRef.current)return 0}return _t})}, 1000);}// 手动清除计时器const clearCountDownTimer = () => clearInterval(timerRef.current)return [time, setCountDown, clearCountDownTimer] as const

}

export default useCountDown

formatRemainTime 格式化剩余时间

根据时间需求:天,时,分和秒定义一个对象数组,包含它们的字符串和对应的时间,遍历该对象数组,处理好天,时,分和秒的情况即可,再在数字前适当添加0即可。

/** 格式化剩余时间 */

export function formatRemainTime(time?: number) {// 当初始化时间为 undefined 时返回if(time === void 0) return '0'const addZero = (n: number) => n >= 10 ? n : ('0' + n)const timeArr: {s: string, t: number}[] = [{s: '天', t: 86400},{s: '时', t: 3600},{s: '分', t: 60},{s: '秒', t: 1},]time = Math.ceil(time / 1000) let res = ''for(let i = 0; i < timeArr.length - 1; i++) {const item = timeArr[i]if(time >= item.t) {const tartget = ~~(time / item.t)res += (!i ? tartget : addZero(tartget)) + item.stime %= item.t}}res += addZero(~~(time)) + timeArr.at(-1)!.sreturn res

}

总结

回看整个倒计时的代码,感觉代码其实是挺简单的,但是我当时实现起来是花了挺长时间的,主要原因还是我当时对 hooks 不太熟悉,现在回看也觉得写得很一般,不是那么的 hooks;相信很多大牛都能写得比我好很多,我这里只是分享一下自己曾经写的一个小组件。大家可以按需修改使用。

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。

有需要的小伙伴,可以点击下方卡片领取,无偿分享

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: