Avoid super calls in controller subclasses

Hi, I am relatively new to Stimulus (and Rails itself) and a real noob here in the forum, so please be patient :slight_smile:

At my current job, I developed a Stimulus controller that should be the parent class of every other controller.
This generic controller should serve 3 main functions: add and setup logging (like disable on prod, enable it for debug at runtime), setup error reporting (ie: add context), add the instance object in the element for easy reachability.

The DOM element link needs to happen in the connect and needs to be removed in the disconnect method.
Now, I’m concerned that it could be too easy to forget super.connect or super.disconnect, and if that happens, we won’t know until an app error is thrown.

I thought about abstracting the super.* calls in some way, like suggested here, but that just feels wrong, (the setInterval solution at the bottom… I simply don’t like it)

So I tried wrapping the initialize/connect/disconnect calls with functions in the parent contructor. Here is some code:

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  constructor(context) {
    super(context)
    // Replace the current instance methods with wrapper functions
    this.initialize = this_initialize.bind(this, this.initialize)
    this.connect = this._connect.bind(this, this.connect)
    this.disconnect = this._disconnect.bind(this, this.disconnect)
  }

  _initialize(initializeFn) {
    if (typeof initializeFn === "function") {
      return initializeFn.call(this)
    }
  }

  _connect(connectFn) {
    this.element[this.identifier] = this
    if (typeof connectFn === "function") {
      return connectFn.call(this) // call the passed function
    }
  }

  _disconnect(disconnectFn) {
    // Remove the reference, also to avoid memory leaks
    // just in case the DOM node is retained somewhere, the controller would be too
    if (this.element) {
      delete this.element[this.identifier]
    }
    if (typeof disconnectFn === "function") {
      return disconnectFn.call(this)
    }
  }
}

I obviously tested this with a couple of controllers and it works just fine.

Do you see any problem in this implementation?
Wrapping the methods at runtime it’s not a great thing, it’s not very obvious where things are coming from when someone reads the code, but I like it more than everything else I found.