Cleaning up events in disconnectedCallback
Older article, still good but see Cleaning up events on disconnectedCallback
Backstory
In my first major native web components project was not a SPA. It was mainly forms with a shared header, footer, modals, etc. If I had a memory leak, no big deal, even if a user failed at filling out a form 50 different times, it would not ‘leak’ enough to matter.
But now I am working on a SPA and making re-usable components where a user might sit on the app all day long without refreshing the browser. Cleanup matters.
Issues with removeEventListener
JavaScript removeEventListener is not as straight forward as one would initially think. Read the MDN docs for more details. But in short, removeEventListener has to try to find and match the right event to remove. This task becomes easier if you tell it which event to remove.
The code below will not work because add .bind(this) creates a new function instance, and the two are not the same. https://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind
connectedCallback(){
this.innerHTML = '<button>Close</button>
this.closeButton = this.querySelector(':scope button')
this.closeButton.addEventListener('click', this.closeHandler.bind(this),{once:true})
}
disconnectedCallback(){
this.closeButton.removeEventListener('click', this.closeHandler.bind(this), {once:true})
}
closeHandler(e:PointerEvent){
this.classList.add('hidden')
}
One simple solution: Make a property to hold a reference to the bound object.
https://stackoverflow.com/questions/54921027/remove-event-listener-that-has-been-added-using-bindthis
connectedCallback(){
this.innerHTML = '<button>Close</button>
this.closeButton = this.querySelector(':scope button')
this.closeHandlerBound = this.closeHandler.bind(this) // the fix
this.closeButton.addEventListener('click', this.closeHandlerBound, {once:true})
}
disconnectedCallback(){
this.closeButton.removeEventListener('click', this.closeHandlerbound)
}
closeHandler(e:PointerEvent){
this.classList.add('hidden')
}
Ensuring this strategy works
Check to see if the event count is stays the same using the Memory tab in Chrome dev tools.
Before: 20 events Events: 22 events After: 20 events
Safe to say this solution works
Before events are added
After events are added
After events are removed