/stimulus_reflex

Build reactive Single Page Applications (SPAs) with Rails and Stimulus

Primary LanguageRubyMIT LicenseMIT

Lines of Code Maintainability

StimulusReflex

This project supports building reactive applications with the Rails tooling you already know and love. It's designed to work perfectly with server rendered HTML, Russian doll caching, Stimulus, Turbolinks, etc...

No need for a complex front-end framework. No need to grow your team or duplicate your efforts.

Inspired by Phoenix LiveView. 🙌

Table of Contents

Before you Begin

StimulusReflex provides functionality similar to what can already be achieved with Rails by combining UJS remote elements , Stimulus, and Turbolinks. Consider building with standard Rails tooling before introducing StimulusReflex. Check out the Stimulus TodoMVC example if you are unsure how to accomplish this.

StimulusReflex offers 3 primary benefits over the traditional Rails HTTP request/response cycle.

  1. Communication happens on the ActionCable web socket - saves time by avoiding the overhead of establishishing traditional HTTP connections
  2. The controller action is invoked directly - skips framework overhead such as the middleware chain, etc...
  3. DOM diffing is used to update the page - provides faster rendering and less jitter

How it Works

  1. Render a standard Rails view template
  2. Use Stimulus and ActionCable to invoke a method on the server
  3. Watch the page automatically render updates via fast DOM diffing
  4. That's it...

Yes, it really is that simple. There are no hidden gotchas.

How it Works

Setup

JavaScript

yarn add stimulus_reflex

Gemfile

gem "stimulus_reflex"

Basic Usage

app/views/pages/example.html.erb

<head></head>
  <body>
    <a href="#" data-controller="example" data-action="click->example#increment">
      Increment <%= @count.to_i %>
    </a>
  </body>
</html>

app/javascript/controllers/example.js

import { Controller } from "stimulus"
import StimulusReflex from "stimulus_reflex"

export default class extends Controller {
  connect() {
    StimulusReflex.register(this);
  }

  increment() {
    // trigger a server-side reflex and a client-side page update
    this.stimulate('ExampleReflex#increment', 1);
  }
}

app/reflexes/example_reflex.rb

class ExampleReflex < StimulusReflex::Reflex
  def increment(step = 1)
    @count = @count.to_i + step
  end
end

The following happens after the StimulusReflex::Reflex method call finishes.

  1. The page that triggered the reflex is re-rerendered. Instance variables created in the reflex are available to both the controller and view templates.
  2. The re-rendered HTML is sent to the client over the ActionCable socket.
  3. The page is updated via fast DOM diffing courtesy of morphdom. While future versions of StimulusReflex might support more granular updates, today the entire body is re-rendered and sent over the socket.

Advanced Usage

ActionCable

StimulusReflex will use the ActionCable defaults of window.App and App.cable if they exist. If these defaults do not exist, StimulusReflex will establish a new socket connection.

Performance

ActionCable emits verbose log messages. Disabling ActionCable logs may improve performance.

# config/application.rb

ActionCable.server.config.logger = Logger.new(nil)

ActionCable Rooms

You may find the need to restrict notifications to a specific room. This can be accomplished by setting the data-room attribute on the StimulusController element.

<a href="#" data-controller="example" data-action="click->example#increment" data-room="12345">

Render Delay

An attempt is made to reduce repaint/reflow jitter when users trigger lots of updates.

You can control how long to wait (think debounce) prior to updating the page. Simply set the renderDelay (milliseconds) option when registering the controller.

export default class extends Controller {
  connect() {
    StimulusReflex.register(this, {renderDelay: 200});
  }
}

The default value is 25.

Demo Applications

Building apps with StimulusReflex should evoke your memories of the original Rails demo video.

Look at all the things I'm not doing. -DHH

Contributing

This project uses Standard and Prettier to minimize bike shedding related to code formatting. Please run ./bin/standardize prior submitting pull requests.