I have an extension that was build with react (legacy code), and I have been tracking a bug that I have finally cornered, but I cannot fixed.
When the icon of the extension (in the browser bar) is clicked a react Component
is created, and a listener is added in its 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,
})
}
However, I cannot figure out how to remove the listener once the Component
disappear, e.g. by clicking somewhere else in the browser. I thought that componentWillUnmount
was what I looking for, but it is never called:
componentWillUnmount(){
// this is never called!!!
background_object.event.removeListener('onDance', this.dance);
}
The problem is that everytime I open (and close) the extension popup, a new event is added to the background_object
, so that dance()
is called multiple times (as many as I open and close the popup).
For now, I have used once
instead of 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,
})
}
In this way, at least, it's only called once. However, I am concerned that my component is being created multiple times and consuming memory in my browser.
How can I make sure that the component is actually being destroyed? How can I detect when the popup is closed in order to remove the event?
It is possible to use chrome.runtime.onConnect
for this (thanks @wOxxOm):
constructor(props){
super(props)
this.state = {
dance: false,
}
...
var port = this.xbrowser.runtime.connect();
...
}
componentDidMount
of the react Component.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,
})
}
background.js
) listen to the connections to the browser, and remove the event when the connection is lost:chrome.runtime.onConnect.addListener(function (externalPort) {
externalPort.onDisconnect.addListener(function () {
let background_object = this.props.getBackgroundObject();
background_object.event.removeListener('onSend');
})
})
In my head, this is not very elegant, but it is doing the trick.