最近有一個需求是做一個閃購列表,列表中每一個商品都有倒計時,如果每一個倒計時都去生成一個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> ); } }