弹出窗口关闭时如何删除扩展弹出窗口(React 组件)的侦听器?

How to remove a listener of an extension popup (React component), when the popup is closed?

我有一个用 React 构建的扩展(遗留代码),我一直在跟踪一个我终于解决了的错误,但我无法修复。

单击扩展图标(在浏览器栏中)时,会创建一个反应 Component,并在其 componentDidMount() 中添加一个侦听器:

async componentDidMount(){
   ...
   // an object from the background is retrieved
   let background_object = this.props.getBackgroundObject();
   ...
   // code including await background_object.doSomething();
   ...
   // add event (eventemitter3 is used for the event management)
   background_object.event.on('onMusic', this.dance);
   ...
}

async dance() {
  this.setState({
    'music': true,
  })
}

但是,一旦 Component 消失,我不知道如何删除监听器,例如通过单击浏览器中的其他位置。我以为 componentWillUnmount 是我要找的东西,但它从来没有被称为:

componentWillUnmount(){
  // this is never called!!!
  background_object.event.removeListener('onDance', this.dance);
}

问题是每次我打开(和关闭)扩展弹出窗口时,都会向 background_object 添加一个新事件,因此 dance() 会被多次调用(与我打开的次数一样多)并关闭弹出窗口)。

目前,我使用 once 而不是 on:

async componentDidMount(){
   ...
   // an object from the background is retrieved
   let background_object = this.props.getBackgroundObject();
   ...
   // code including await background_object.doSomething();
   ...
   // add event (eventemitter3 is used for the event management)
   background_object.event.once('onMusic', this.dance);
   ...
}

async dance() {
 // add the event again in case onMusic is called again
 background_object.event.once('onMusic', this.dance);
 this.setState({
   'music': true,
 })
}

这样一来,至少只调用了一次。但是,我担心 我的组件被多次创建 并在我的浏览器中消耗内存。

如何确保组件确实被销毁了?如何检测弹出窗口何时关闭以删除事件?

可以为此使用 chrome.runtime.onConnect(感谢@wOxxOm):

  1. 在 React 组件的构造函数中打开一个连接:
  constructor(props){
    super(props)
    this.state = {
      dance: false,
    }
    ...
    var port = this.xbrowser.runtime.connect();
    ...
  }

  1. 在react组件的componentDidMount中添加事件
async componentDidMount(){
   ...
   // an object from the background is retrieved
   let background_object = this.props.getBackgroundObject();

   ...
   // add event (eventemitter3 is used for the event management)
   background_object.event.on('onMusic', this.dance);
   ...
}

async dance() {
  this.setState({
    'music': true,
  })
}
  1. 在后台某处(例如background.js)监听与浏览器的连接,并在连接丢失时删除事件:
chrome.runtime.onConnect.addListener(function (externalPort) {
  externalPort.onDisconnect.addListener(function () {
     let background_object = this.props.getBackgroundObject();
     background_object.event.removeListener('onSend'); 
  })
})

在我看来,这不是很优雅,但它确实有用。