You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React类组件中componentWillUnmount无法移除返回箭头函数的事件监听?

React类组件事件监听移除失效问题解析

我在开发React类组件时,在componentDidMount生命周期中通过addEventListener添加事件监听,但遇到了组件卸载时监听无法正常移除的问题。以下是两种写法的对比:

问题写法(无法移除监听)

componentDidMount() {
  document.addEventListener('copy', this.copyBlock('copy'));
  document.addEventListener('cut', this.copyBlock('cut'));
}

componentWillUnmount() {
  document.removeEventListener('copy', this.copyBlock('copy'));
  document.removeEventListener('cut', this.copyBlock('cut'));
}

// 接收copyType并返回事件处理箭头函数
copyBlock = (copyType: 'copy' | 'cut') => (e: ClipboardEvent) => {
  if (e.target !== document.body) {
    return;
  }
  const {
    type,
    libraryStore: { showUtilBlock },
  } = this.props;

  BlockUtil.copyBlock(e, copyType);
};

这种写法下,组件卸载时无法正常移除事件监听。

可行写法(可正常移除监听)

componentDidMount() {
  document.addEventListener('copy', this.copyBlock);
}

componentWillUnmount() {
  document.removeEventListener('copy', this.copyBlock);
}

copyBlock = (e?: ClipboardEvent) => {
  if (e.target !== document.body) {
    return;
  }
  const {
    type,
    libraryStore: { showUtilBlock },
  } = this.props;

  BlockUtil.copyBlock(e, 'copy');
};

差异与原因解析

1. 函数引用的唯一性

  • 问题写法中,this.copyBlock('copy')是高阶函数调用,每次执行都会返回一个全新的箭头函数。componentDidMount里添加的是第一次调用生成的箭头函数,而componentWillUnmount里再次调用this.copyBlock('copy'),会生成另一个完全独立的箭头函数——二者的函数引用完全不同。
  • 可行写法中,this.copyBlock是类的箭头函数属性,组件实例化后这个函数的引用就固定了,添加和移除监听时用的是同一个函数引用。

2. removeEventListener的匹配规则

浏览器的removeEventListener要求必须传入与添加监听时完全相同的函数引用,才能找到对应的监听并移除。由于JavaScript中函数是引用类型,即使两个函数的逻辑完全一致,只要不是同一个引用,就会被视为不同的函数。

问题写法中,添加和移除时的函数引用不匹配,所以浏览器找不到要移除的监听,导致监听残留;可行写法用了同一个引用,因此能正常移除。

优化方案(保留高阶函数设计)

如果想保留copyBlock接收copyType的设计,可以在组件实例中缓存生成的事件处理函数,确保添加和移除时用同一个引用:

componentDidMount() {
  // 将生成的函数缓存到实例属性
  this._copyHandler = this.copyBlock('copy');
  this._cutHandler = this.copyBlock('cut');
  document.addEventListener('copy', this._copyHandler);
  document.addEventListener('cut', this._cutHandler);
}

componentWillUnmount() {
  // 使用缓存的引用移除监听
  document.removeEventListener('copy', this._copyHandler);
  document.removeEventListener('cut', this._cutHandler);
}

copyBlock = (copyType: 'copy' | 'cut') => (e: ClipboardEvent) => {
  if (e.target !== document.body) {
    return;
  }
  const {
    type,
    libraryStore: { showUtilBlock },
  } = this.props;

  BlockUtil.copyBlock(e, copyType);
};

内容的提问来源于stack exchange,提问作者GoonGamja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 21:05:16