Refactor Stimulus.js Controllers to Use Change Callbacks
Stimulus’ values API has a great change callback function. This allows you to respond whenever a value changes. 🦉
For a quick refresher: the values API allows you read (and write!) data attributes to the controller’s element.
You define them like so in the HTML: <div data-controller="counter" data-counter-time-value="10">10</div>.
Then in the controller define them in the static values:
export default class extends Controller {
static values = { time: Number }
}
With above controller you might expect a connect function to start the timer.
// app/javascripts/controllers/counter_controller.js
export default class extends Controller {
static values = { time: Number }
connect() {
this.timer = setInterval(() => {
this.timeValue--;
if (this.timeValue > 0) {
this.element.textContent = this.timeValue;
} else {
this.element.textContent = "Time's up!"
clearInterval(this.timer);
}
}, 1000);
}
}
And above might look just fine to you. But I like to keep my functions smaller in scope and I am pretty sure the next requirement is to add a stop and pause feature (and I need a bridge to show how the changes callbacks can be used).
So let’s do a little refactor using Change Callbacks.
export default class extends Controller {
static values = { time: Number }
connect() {
this.timer = null;
this.#start();
}
// private
timeValueChanged() {
if (this.timeValue > 0) {
this.element.textContent = this.timeValue;
} else {
this.element.textContent = "Time's up!";
clearInterval(this.timer);
}
}
#start() {
this.timer = setInterval(() => {
this.timeValue--;
}, 1000);
}
}
timevalueChanged() is called whenever the this.timeValue is changed (which is done in #start().
Is this better? “The class has gotten bigger!” But I’d argue it is better. Yes, significantly more lines of code, but to me it’s easier to follow. The #start function is only concerned with starting the timer. You can imagine there being a public function, alongside a stop() and pause() function.
- show a maximum allowed character count;
- fetch API endpoint when urlValue changes;
- show the selected option from a select-element;
- disable a button on “loading” value.
It is one of those, somewhat, hidden helper functions in Stimulus that if you know it, you see ways to use it in many situations.
How have you used change callbacks in Stimulus controllers? Let me know about them.
Want to read me more?
-
Create an Animated Counter in Stimulus
Create a simple animated counter with Stimulus. Follow this step by step tutorial to create a small, reusable Stimulus controller. -
Connected and Disconnected Target Callbacks with Stimulus
With the Connected and Disconnected callbacks for targets in Stimulus, you can run code when targets are added and removed from the DOM. -
How to Properly Structure Stimulus Controller
Guideline on how to properly structure your Stimulus controller.
Over to you…
What did you like about this article? Learned something knew? Found something is missing or even broken? 🫣 Let me (and others) know!
Comments are powered by Chirp Form
{{comment}}