场景重现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