Turbo Integration Issue with Stimulus Controllers: Seeking Expertise to Resolve

Greetings everyone,

I wanted to share an issue we’ve encountered while integrating Turbo into our Symfony Vs Stimulus setup.

Following the addition of Turbo, we’ve noticed a peculiar behavior: upon Turbo replacing the body of the page, all Stimulus controllers rerun, as expected. However, unexpectedly, all event listeners are duplicated.

Upon inspecting the console, we’ve observed that controllers are disconnecting and then reconnecting. We hadn’t implemented unsubscription on the disconnect event for any addEventListener previously, as it wasn’t necessary due to full page reloads.

Our assumption was that replacing the body would discard former event listeners, but this doesn’t seem to be the case.

While we’re excited about incorporating Turbo into our app, these critical issues it introduces are concerning. We’re hopeful that with your expertise, we can find solutions to address these challenges.

Thank you for your attention and support.

I believe this is expected behavior. During disconnect, you should clean up any listeners you added during connect.

Alternatively, you could setup your listeners in an initialize method. This runs only once, even if there are multiple connect/disconnect cycles.

Lastly though, I consider addEventListener within a Stimulus controller to be a code smell. That doesn’t mean that it is never called for, but it does mean that it should be done rarely. Stimulus controller side effects should be readable from the DOM, without opening the controller. Try moving your listeners’ behavior into data-action attributes.

In other words, instead of writing addEventListener('some-event', (event) => /* do something */) in your connect method. Add a function like

myFunction(event) {
  /* do something */
}

and then declare the connection in the DOM via data-action like

<div data-action="some-event->controller-name#myFunction"></div>