場景重現react
以前在作一個IM
的模塊,有一個撤回消息後兩分鐘以內能夠從新編輯
的功能,項目是用react
作的,按照正常的思路,只需傳入一個消息撤回的時間markTime
,而後用如今時間Date.now()
去判斷是否已經超時兩分鐘。然而理想老是美好的,因爲沒有去觸發react
的從新渲染機制,即便超時了從新編輯
的按鈕也不會消失,等到去點擊從新編輯
按鈕再提示已超時的話這個極大的影響用戶體驗。性能優化
解決方案性能
能夠給每條撤回的系統消息加上一個setInterval
定時器輪詢,只要未超時就實時更新當前時間,直至超時再把這個定時器清除掉;
可是這個方法有個弊端,就是假設我在有限時間內撤回了很是多條消息,那麼這很是多條的消息就會有着對應的多個定時器在工做着,這對於性能的損耗特別的大,對於本公司的項目,團隊在性能優化上仍是有追求的,因此思路I被否決掉了;優化
經過靜態方法維護同一個定時器訂閱器,若是該訂閱器中還有存在的未超過有限時間的事件,則展現其中未超過有限時間的子組件,若是已超時,則不展現其對應的自組件,返回一個null
。
上代碼ui
/** * 經過靜態方法維護同一個定時器,傳入標記時間markTime和在這段有效時長alidTimeLong內,2000毫秒更新一次組件,若是超過有效時長則返回null */
import React from "react";
type UpDateFunc = (now: number) => boolean;
// 使用該組件時,須要傳入兩個參數,一個開始定時的時間,一個有效時長,都爲number類型
export default class TimePlay extends React.Component<{
markTime: number;
validTimeLong: number;
}> {
// 維護一個Set的訂閱器
static countDownFuncList = new Set<UpDateFunc>();
// 定時器的初始狀態
static intervalHandel = -1;
// 往訂閱器中添加一個須要監聽的事件
static addFunc = (func: UpDateFunc) => {
TimePlay.countDownFuncList.add(func);
TimePlay.countDown();
};
// 訂閱器中刪除一個超時的事件
static removeFunc = (func: UpDateFunc) => {
TimePlay.countDownFuncList.delete(func);
};
// 訂閱器中若是還存在事件,則在同一個定時器下執行
static countDown = () => {
if (TimePlay.intervalHandel !== -1) {
return;
}
TimePlay.intervalHandel = setInterval(() => {
if (TimePlay.countDownFuncList.size === 0) {
clearInterval(TimePlay.intervalHandel);
TimePlay.intervalHandel = -1;
}
const now = Date.now();
for (const fn of TimePlay.countDownFuncList) {
const isValid = fn(now);
if (!isValid) {
TimePlay.removeFunc(fn);
}
}
}, 2000);
};
// 判斷是否展現子組件
state = {
isShowChildren: this.props.markTime + this.props.validTimeLong > Date.now(),
};
// 用於初始時判斷是否超時
updateState = (now: number) => {
const isValid = this.props.markTime + this.props.validTimeLong > now;
if (isValid && !this.state.isShowChildren) {
this.setState({ isShowChildren: true });
}
if (!isValid) {
this.setState({ isShowChildren: false });
}
return isValid;
};
componentDidMount() {
TimePlay.addFunc(this.updateState);
}
render() {
if (this.state.isShowChildren) {
return this.props.children;
} else {
return null;
}
}
}
複製代碼
經過此方法,若是在實際業務當中兩分鐘內撤回了不少條消息,依舊只須要靜態方法中的一個定時器去輪詢,而無需同時開啓多個定時器。this
固然,若是對render中的return結果進行改造,還能夠應用於搶購或其餘場景下同個頁面有多個倒計時的場景。spa
若是你有更好的解決方案,歡迎討論~code