React类组件使用setTimeout延迟卸载组件时clearTimeout失效问题
问题根因
核心问题出在构造函数的执行顺序上:
你在constructor中先执行this._delay()设置定时器,将返回的定时器ID赋值给了this.timerS,紧接着又执行this.timerS = null把有效ID直接覆盖成了空值。这就导致你第一次调用stop()时,clearTimeout接收的参数是null,完全没有清除最初创建的那个定时器。
当你注释掉unmountComponentAtNode时,这个漏清的定时器执行后只会打印日志,没有明显的副作用,所以你误以为clearTimeout生效了;一旦启用卸载逻辑,这个没被清理的定时器会在2400ms后触发组件卸载,看起来就像clearTimeout被忽略了一样。
此外还有两个容易引发问题的逻辑缺陷:
- 调用
start方法时没有先清空已有定时器,用户快速悬浮、移出多次会生成多个并行的定时器,最终还是会触发组件卸载 - 没有在组件卸载时清理定时器,会存在内存泄漏风险,也可能触发已卸载组件的状态更新报错
修复方案
- 调整构造函数的初始化顺序,先初始化
timerS再调用延迟方法 start方法新增前置清理逻辑,避免重复创建定时器- 新增组件卸载生命周期的定时器清理逻辑
修改后的完整参考代码:
class Notify extends React.Component { constructor(props){ super(props) this.class = 'check' this.state = {load:LineNotify[this.class].anime} // 先初始化timerS,再调用_delay生成定时器 this.timerS = null this._delay() this.box = BoxNotify[this.class] this.icon = IconNotify[this.class] } remove=()=>{ unmountComponentAtNode(document.getElementById('notify')) } _delay=()=>{ this.timerS = setTimeout(this.remove,2400) } stop=()=>{ clearTimeout(this.timerS) this.setState({load:LineNotify[this.class].line}) } start=()=>{ // 每次启动前先清掉旧定时器,避免多定时器并行 clearTimeout(this.timerS) this.timerS = setTimeout(this.remove,2400) this.setState({load:LineNotify[this.class].anime}) } componentWillUnmount() { // 组件卸载时清理定时器,避免内存泄漏 clearTimeout(this.timerS) } }
内容的提问来源于stack exchange,提问作者rick
相关产品推荐
相关产品推荐

