Software Development
Pawel Muszynski
Pawel Muszynski
Software Engineer
2021-06-22

StimulusReflex – a quick way to create reactive apps – an introduction

What is StimulusReflex? Quite popular nowadays HTML over-the-wire approach led us to the creation of frameworks and libraries which send HTML over the wire instead of using JSON. StimulusReflex is one of them.

It is new way to create reactive user interface in Ruby on Rails. StimulusReflex extends the capabilities of Rails and Stimulus by capturing user interactions and passing them to Rails over real-time websockets. Pages are quickly re-rendered and all updates are sent to the client via CableReady.

CableReady allows to create real-time updates by triggering client-side DOM changes, events and notifications via ActionCable. Unlike Ajax, operations are not always initiated by the user other browser, for example, they can also be initiated by workers.

StimulusReflex was originally inspired by Phoenix’s LiveView (an alternative to the SPA). StimulusReflex’s goal has always been to make building modern apps with Rails the most productive and enjoyable option available. And in my opinion, this is exactly what they achieved here.

Why should we use StimulusReflex?

It’s simple, to focus on developing a product instead of introducing consistent changes in modern JavaScript. Also, StimulusReflex applications have simple, concise and clear code and integrate seamlessly with Ruby on Rails. This allows small RoR teams to do big things even without great knowledge of React, Vue or their modern JavaScript solutions.

How does StimulusReflex works?

Reflex

Reflex is a transactional UI update that takes place over a persistent open connection to the server. StimulusReflex maps requests to Reflex class. Reflex classes are in the app/reflexes directory.

class PostReflex < ApplicationReflex
end

If we create Reflex class with a generator, we can see that our class contains this comment:

 # All Reflex instances include CableReady::Broadcaster and expose the following properties:
 #
 #   - connection – the ActionCable connection,
 #   - channel – the ActionCable channel,
 #   - request – an ActionDispatch::Request proxy for the socket connection,
 #   - session – the ActionDispatch::Session store for the current visitor,
 #   - flash – the ActionDispatch::Flash::FlashHash for the current request,
 #   - url – the URL of the page that triggered the reflex,
 #   - params – parameters from the element’s closest form (if any),
 #   - element – a Hash-like object that represents the HTML element that triggered the reflex,
 #     - signed – uses a signed Global ID to map dataset attributed to a model e.g., element.signed[:foo],
 #     - unsigned – uses an unsigned Global ID to map dataset attributed to a model e.g., element.unsigned[:foo],
 #   - cable_ready – a special cable_ready that can broadcast to the current visitor (no brackets needed),
 #   - reflex_id – a UUIDv4 that uniquely identifies each Reflex.

As we can see, there are few properties that can be used in our class. The most interesting at the beginning will be the element property that contains all of the Stimulus controller’s DOM element attributes as well as other properties, like tagName, checked and value.

StimulusReflex also gives us a set of callbacks to allow us to control our reflex’s processes:

  • before_reflex
  • around_reflex
  • after_reflex

As you can see, the naming is very similar to Active Record Callbacks.

Declaring a Reflex in HTML with data attribute

The fastest way to enable Reflex actions is by using the data-reflex attribute. The syntax follows the Stimulus format: [DOM-event]->[ReflexClass]#[action]

<button
 data-reflex="click->Post#increment"
 data-post-id="<%= post.id %>"
>Like</button>

In this example, the data-reflex attribute pointed out the PostRefex class and the increment method on the click event. Here we also passed data-post-id that we can later use in the Reflex class through element.dataset[:post_id].

class PostsReflex < ApplicationReflex
 def increment
   post = Post.find(element.dataset[:post_id])

   post.increment! :likes
 end
end

Morphs

By default, StimulusReflex updates the entire page (Page morph). After re-processing the controller action, rendering the view template and sending the raw HTML to your browser, StimulusReflex uses the morphdom library to do the smallest number of DOM modifications necessary to refresh your UI in just a few milliseconds.

StimulusReflex features three distinct modes of operation:

Page Morph – performs a full-page update, Selector Morph – replaces the content of an element, Nothing Morph – executes functions that do not update your page (for example, calling your employee).

To change our PostReflex#increment method, we can simply use the morph keyword and target the partial that we want to update.

def increment
 post = Post.find(element.dataset[:post_id])
 post.increment! :likes

 morph "#posts_#{post.id}", render(partial: 'post', locals: { posts: post })
end

My thoughts

This quite a short introduction is enough to start your journey with reactive Rails using StimulusReflex. Isn't it great to be able to create a reactive SPA app with just a few Ruby lines and no JavaScript? For me, this whole HTML over-the-wire idea is exciting, and I will definitely dig into this topic in the future. For now, I highly recommend to you StimulusReflex documentation.Shoutout to all people involved into the development of StimulusReflex!

Get free code review

Read more:

Why you should (probably) use Typescript

How not to kill a project with bad coding practices?

Data fetching strategies in NextJS