Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clear dangling EventListeners and Detached Nodes when a controller is…
… removed from the DOM `eventListenerMaps` maps HTML elements handled by Stimulus to `eventListenerMap`, a map of Stimulus EventListeners in this way: `{ HTMLelement: { eventName: EventListener } }`. When a controller HTML is removed from the DOM the dangling EventListeners aren't removed, moreover, the removed HTML elements are still referenced via the `eventListenerMaps` keys. This leads to having multiple detached HTMLelements and eventListeners which can't be GCed hence a memory leak. The leak is fixed by removing the dangling eventListeners and clearing unused `eventListenerMaps` keys. When the HTMLelement is attached again to the DOM, the `eventListenerMaps` and the related `eventListeners` are automatically re-created by Stimulus via the MutationObservers, no data is lost by doing this. Below is an example to reproduce the issue: ``` <html> <head> <title>EventListenerMaps memory leak</title> <script type="module"> import { Application, Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js" window.Stimulus = Application.start() Stimulus.register("boosts", class extends Controller { doClick() { process(500) } }) </script> <script> function process(count) { let i = 0 let handler = setInterval(function(){ if (++i > count) { clearInterval(handler) } else { document.body.replaceWith(document.body.cloneNode(true)) } }, 1) } </script> </head> <body> <div data-controller="boosts"> To reproduce: <ul> <li>Check heap snapshot</li> <li>Click "trigger leak" button</li> <li>Check heap snapshot again</li> </ul> <button data-action="click->boosts#doClick">trigger leak</button> </div> </body> </html> ```
- Loading branch information