Jonathan Bennett

Fixing this in event handlers

If you’re using Stimulus or modern JavaScript classes, you’ve probably hit this:

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
	connect() {
		this.element.addEventListener("click", this.clicked)
	}

	clicked() {
		console.log(this) // Window?! It should be the controller.
	}
}

JavaScript loves to mess with this, and in this case, it’s pointing to window, not your controller.

The Usual Fix: Manual Binding

The usual advice is to bind your method in connect():

connect() {
	this.clicked = this.clicked.bind(this)
	this.element.addEventListener("click", this.clicked)
}

Sure, it works. But also… ew.

A Cleaner Way: Arrow Function Class Fields

Here’s what I prefer — define the method as an arrow function:

clicked = () => {
	console.log(this) // Controller instance 🎉
}

Now connect() stays nice and clean, like we originally intended:

connect() {
	this.element.addEventListener("click", this.clicked)
}

No binding. No surprises. It just works.™