最近有一個需求是做一個閃購列表,列表中每一個商品都有倒計時,如果每一個倒計時都去生成一個setTimeout的話,一個頁面就會有很多定時器,感覺這種做法不是非常好,於是換了一個思路。
思路是這樣的,一個頁面只生成一個定時器。頁面利用對象去維護一個回調函數列表,key可以是id等唯一標識,value就是更新時間的函數,我這里用的是setState。提供一個往對象里添加回調函數的方法和一個移除回調函數的方法。
// 用於存放每個倒計時的回調方法
const countDownFuncList = {};
const addFunc = (key, func) => {
countDownFuncList[key] = func;
}
const removeFunc = (key) => {
delete countDownFuncList[key];
}
生成一個定時器,隔一定的時間就去遍歷回調函數列表,調用里面的函數。
let intervalHandler = -1;
const countDown = () => {
if (intervalHandler !== -1) {
clearTimeout(intervalHandler);
}
intervalHandler = setTimeout(() => {
const now = new Date();
Object.keys(countDownFuncList).forEach((key) => {
const item = countDownFuncList[key];
if (typeof item === 'function') {
item(now);
}
})
}, 300);
}
具體調用是調用timeContent方法來處理展示的時間。
const timeContent = (millisecond) => {
const second = millisecond / 1000;
let d = Math.floor(second / 86400);
let h = Math.floor((second % 86400) / 3600);
let m = Math.floor(((second % 86400) % 3600) / 60);
let s = Math.floor(((second % 86400) % 3600) % 60);
let countDownDOM;
if (d > 0) {
countDownDOM = (<div class="count-down">{d} 天 {h} : {m} : {s}</div>);
} else {
countDownDOM = (<div class="count-down">{h} : {m} : {s}</div>);
}
return countDownDOM;
}
這個方法有一個缺點就是當前時間的獲取,除了初始化步驟以外,之后的更新都是通過new Date()來獲取的,這樣存在獲取的時間可能並不是正確的當前時間的問題。
完整代碼如下:
// 用於存放每個倒計時的回調方法
const countDownFuncList = {};
const addFunc = (key, func) => {
countDownFuncList[key] = func;
}
const removeFunc = (key) => {
delete countDownFuncList[key];
}
const timeContent = (millisecond) => {
const second = millisecond / 1000;
let d = Math.floor(second / 86400);
let h = Math.floor((second % 86400) / 3600);
let m = Math.floor(((second % 86400) % 3600) / 60);
let s = Math.floor(((second % 86400) % 3600) % 60);
let countDownDOM;
if (d > 0) {
countDownDOM = (<div class="count-down">{d} 天 {h} : {m} : {s}</div>);
} else {
countDownDOM = (<div class="count-down">{h} : {m} : {s}</div>);
}
return countDownDOM;
}
let intervalHandler = -1;
const countDown = () => {
if (intervalHandler !== -1) {
clearTimeout(intervalHandler);
}
intervalHandler = setTimeout(() => {
const now = new Date();
Object.keys(countDownFuncList).forEach((key) => {
const item = countDownFuncList[key];
if (typeof item === 'function') {
item(now);
}
})
}, 300);
}
countDown();
class CountDownItem extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTime: this.props.nowDate
}
this.parseDisplayTime = this.parseDisplayTime.bind(this);
}
componentDidMount() {
const { id } = this.props;
// 往事件列表添加回調函數
addFunc(id, this.updateTime);
}
componentWillUnmount() {
const { id } = this.props;
// 從事件列表移除回調函數
removeFunc(id);
}
updateTime(time) {
this.setState({
currentTime: time
})
}
parseDisplayTime() {
const { endTime, id } = this.props;
const { currentTime } = this.state;
const subtractTime = endTime - currentTime;
let countDownDOM = '';
if(subtractTime > 1000){
countDownTimeDOM = (
<div className="count-down-content">
{timeContent(subtractTime)}
</div>
);
}else{
removeFunc(id);
}
return countDownDOM;
}
render(){
return(
<div className="count-down-wrap">{this.parseDisplayTime()}</div>
);
}
}
