Troubleshooting
Verify ActionCable
If ActionCable isn't working properly in your environment, StimulusReflex cannot function.
Step one to any troubleshooting process should be "is it plugged in?"
First, run rails generate channel test
in your Rails project folder. This will ensure that your ActionCable setup has been initialized, although you should verify that in your app/javascript/packs/application.js
you have import 'channels'
present.
Next, copy and paste the following into the two specified files, replacing their contents.
class TestChannel < ApplicationCable::Channel
def subscribed
stream_from "test"
end
def receive(data)
puts data["message"]
ActionCable.server.broadcast("test", "ActionCable is connected")
end
end
import consumer from './consumer'
consumer.subscriptions.create('TestChannel', {
connected () {
this.send({ message: 'Client is live' })
},
received (data) {
console.log(data)
}
})
If ActionCable is running properly, you should see ActionCable is connected
in your browser's Console Inspector and Client is live
in your server's STDOUT log stream.
You can feel free to remove both of these files after you're done, but leave app/javascript/channels/consumer.js
where it is so that all of your ActionCable channel subscribers can share one ActionCable connection.
Consider disabling ActionCable logs
Once your app is properly configured and off to a good start, you might want to consider disabling ActionCable logging by setting the following in an initializer:
ActionCable.server.config.logger = Logger.new(nil)
Not only is the ActionCable logger particularly verbose - especially when you're doing full-page morph operations - but it's widely understood to be a source of mystery slowdowns.
Consider reading up on Redis, AnyCable and Heroku
There is an abundance of collected wisdom on the StimulusReflex Deployment documentation page for developers who are working with ActionCable.
Another excellent resource is the Sidekiq wiki page for Heroku.
Remote forms in Rails 6.1
The behaviour of form helpers changed slightly in Rails 6.1, as forms are no longer automatically set to be remote: true
by default. This catches many developers off-guard!
We recommend that Rails developers use UJS/mrujs remote forms wherever possible, especially if they are using Turbolinks / Turbo Drive. This allows forms to be submitted without reloading the page, which is not only much faster (no more ugly screen refreshes!) but allows ActionCable Connections to remain open, too.
<%= form_with model: @foo, local: false %>
<%= form_with model: @foo, data: { remote: "true" } %>
<%= form_for @foo, remote: true %>
<form action="/foo" data-remote="true" method="post"></form>
Things to avoid doing, if possible
Don't include CableReady::Broadcaster
in a Reflex class 🙅♂️
TL;DR: there's some awesome shortcuts for StimulusReflex developers to use CableReady to broadcast operations to the current user, but they come at the expense of introducing a gotcha. We think that they are totally worth it, but we still feel bad when folks occasionally lose time to this.
Don't use CableReady from a standard Rails controller page action
... if the goal is to show the current user something new.
In Rails, the order of operations is Request -> Controller Action -> View Render -> Response. If you broadcast a CableReady operation targeting the current user during the Controller Action phase, it will transmit to the browser and execute before the HTML has even been rendered. This leads to an unfortunate scenario where it appears that "nothing happened."
DANGER
Don't attempt to use sleep
in your Controller Action to "slow down" a CableReady broadcast. Not only will this not work - the same problem will happen, slower - but freezing the Ruby thread means the application server has fewer resources to respond to other requests.
You never want to use sleep
in a primary execution thread. Chances are, you should use an ActiveJob with a delayed start.
Ajax actions, webhooks and broadcasting to other people is fine
If your controller action has created an event that should be broadcast to multiple people, such as an event notification, it makes sense to broadcast that data as soon as it's relevant - ideally, via an ActiveJob so you can return your HTML faster.
Similarly, controller actions that don't initiate a navigation event (Ajax actions, webhooks, OAuth endpoints) are all fine to broadcast.
Note that if this group broadcast would modify the page state of the initiating user, the DOM generated by the Controller Action should reflect the new state as though you also received the broadcast. It's easier to deliver perfect HTML up front, rather than wasting time trying to receive an event on the client. See "The Logical Splitter".
Architecturally, this is like throwing turd away before sprinting to try and catch it. 💩
Don't perform purity rituals
There are some who have been trained to loudly reject what they see as violations of the barrier between business logic and presentation layer in their applications. Much like stop signs and poison labels, the intentions behind these constraints are almost always a really good idea. Most of the time, you should go with the program when it comes to things designed to keep you safe.
There are situations, however, whether the only rational decision is to consciously ignore the generalized advice and be confident that you can explain why you did so. Otherwise, you're just slave to a dogma.
A poignant example of this is the conundrum of an ActiveJob broadcasting HTML updates to the DOM. Which solution sounds sane to you?
- ActiveJob uses CableReady to broadcast a
dispatch_event
operation with a custom event name and resource id attached asdetail
. The event is picked up by a DOM element with a Stimulus controller which immediately callsthis.stimulate('Insane#hoop_jump', id)
which triggers a Selector Reflex that renders a partial and uses CableReady to send amorph
operation which updates the DOM element. - ActiveJob uses CableReady to send a
morph
operation which updates the DOM element.
Don't be the person who performs a Server -> Client -> Server -> Client ritual so that you can claim you kept your business logic separate from filthy presentation layer concerns. That's not architectural purity, it's wasting some of the time you have left before you die. ⏳
radiolabel
If you have Stimulus running on your application, you should consider installing radiolabel. It is a Stimulus controller that watches for CableReady "after-operation" events. When it detects an operation that mutates an element, it will create a titled overlay which briefly announces when an element is modified.
morph operations will be orange, while all others are green.
If you're doing a lot of DOM manipulation with CableReady, you'll find radiolabel to be indispensable.