需求:后台返回截止日期时间戳,根据当前时间,页面回显精确到秒的倒计时。
思路: 抽一个countDown组件,传入截止时间timeStamp,使用setInterval进行倒计时,render页面。
遇到的问题: 在useEffect中,新值只变化一次,没有实时的倒计时。
参考文章: useEffect 完整指南
解决方案:
-
最开始将时分秒作为整体time返回,发现页面没有实时变化。然后将变化后的时间作为useEffcet的依赖,这样页面会动态显示倒计时,但是这样做会一直创建不同的setInterval,不推荐。
-
拆分声明时、分、秒,使用setState的函数形式更新值,只创建一个setInterval。
组件代码如下:
import * as React from "react";
const { useState, useEffect, useRef } = React;
interface IProps {
timeStamp: any;
}
const CountDown = (props: IProps) => {
const { timeStamp } = props;
const intervalRef = useRef<any>(null);
const now: any = Math.round(new Date().getTime() / 1000).toString(); //获取当前时间
const end: any = timeStamp; //设置截止时间
const [leftTime, setLeftTime] = useState(end - now); //时间间隔
const [h, setHours] = useState<any>(""); //小时
const [m, setMinutes] = useState<any>(""); //分钟
const [s, setSeconds] = useState<any>(""); //秒
useEffect(() => {
if (leftTime > 0) {
intervalRef.current = setInterval(() => {
const newNow: any = Math.round(new Date().getTime() / 1000).toString(); // 重新获取当前时间
let newLeftTime = timeStamp - newNow
setLeftTime(() => newLeftTime) //计算新的时间间隔数值
let hours = Math.floor(newLeftTime / 60 / 60 % 24) < 10 ? `0${Math.floor(newLeftTime / 60 / 60 % 24)}` : Math.floor(newLeftTime / 60 / 60 % 24);
let minutes = Math.floor(newLeftTime / 60 % 60) < 10 ? `0${Math.floor(newLeftTime / 60 % 60)}` : Math.floor(newLeftTime / 60 % 60);
let seconds = Math.floor(newLeftTime % 60) < 10 ? `0${Math.floor(newLeftTime % 60)}` : Math.floor(newLeftTime % 60);
setHours(() => hours) //函数写法 设置小时
setMinutes(() => minutes) //函数写法 设置分钟
setSeconds(() => seconds) //函数写法保证值在setInterval里更新,避免useEffect的bug
}, 1000);
} else {
setLeftTime(0)
setHours(0)
setMinutes(0)
setSeconds(0)
clearInterval(intervalRef.current);
}
return () => clearInterval(intervalRef.current);
}, []); //不传依赖
return (
<>
{ leftTime <= 0 && <span>-</span>}
{ leftTime > 0 && <span>{`${h}:${m}:${s}`}</span>}
</>
)
}
export default CountDown;
页面中使用:
import CountDown from './countDown'
<CountDown timeStamp={text} />
暂时记录这些,欢迎指正!