Reactivity Rails with Stimulus Reflex
Tags: rails, stimulus, reflex, reactivity
Published: 2021-03-24 04:26:15

Before talking about `Stimulus Reflex` I want to explain to you in my own words, what´s `Reactivity` in web development.

## What does `Reactivity` mean?

Reactivity is a fast and async reaction to user events just like tools as React or Angular do, for example, when you are typing in a text input and you can see the validation in real-time or when you add a product to a list and it appears immediately without reloading the page, that's reactivity.

## So, What does it mean for Rails to be `Reactive`?

It means that Rails can make interactions in real-time with HTML rendered from the server through WebSockets and the most important, `with the least amount of Javascript code`.

## Let's talk about Stimulus Reflex!

A library that extends the capabilities of both `Rails` and `Stimulus` by intercepting user interactions and passing them to Rails over real-time WebSockets. These interactions are processed by Reflex actions that change the application state. The current page is quickly re-rendered and the changes are sent to the client using `CableReady`.

> Definition from https://docs.stimulusreflex.com/

## Time to code!

This is the repository of [Tasky](https://github.com/Sanchezdav/tasky), the example application of this post.

### Creating the project

We're going to create a simple Tasks Manager for this example 👇🏼 

```bash
# Create a new Rails project with Stimulus
rails new tasky --webpack=stimulus

# Create a new Rails project with Stimulus and a Tailwindcss template
rails new tasky -d postgresql --webpack=stimulus -m https://raw.githubusercontent.com/Sanchezdav/herewego/master/template.rb

# or if you have an existing project without Stimulus
rails webpacker:install:stimulus
```

> For this example I'll be using my template https://github.com/Sanchezdav/herewego,
> with this, we have Users already configured as well.

### Models

The next step is to create our main models, `Project` and `Task`, so we can create a Project and you can add many Tasks for each Project, in this way you can manage and order better your Tasks.

```bash
> rails g scaffold Project user:references name description:text
...
> rails g model Task project:references content
...
```

> Notice that Project is a scaffold because we want all actions to this resource.

Run the migrations to update the database

```bash
> rails db:migrate
```

Once our models are created, we need to add the relationships between them, like this 👇🏼

```ruby
# tasky/app/models/user.rb
class User < ApplicationRecord
  ...
  has_many :projects, dependent: :destroy
end

# tasky/app/models/project.rb
class Project < ApplicationRecord
  belongs_to :user
  has_many :tasks, dependent: :destroy
end

# tasky/app/models/task.rb
class Task < ApplicationRecord
  belongs_to :project
end
```

### Views

At this point, we should be able to create, edit and destroy `Projects`, but if we go to `/projects/1` we just see the `Project` data, so is necessary to add a `Task` form there to create our Tasks list.

So we're going to change the `Project` view like this [file in Github](https://github.com/Sanchezdav/tasky/commit/ab7dbb0bb1003686a6d2411f956eb4d351f1a473#diff-75cd9d125d3e97e7d9aed3f24012ffa9e08fa7df2c2db9888fccc0a1363a76bc) and you'll have something like the next image

![Project view](https://res.cloudinary.com/codeando/image/upload/v1614059255/Screen_Shot_2021-02-22_at_23.47.07_uouou7.png)

### Add Stimulus Reflex

To install `Stimulus Reflex` we need to add the gem and install it

```bash
> bundle add stimulus_reflex
...
> rails stimulus_reflex:install
...
```

Be sure that you have `<%= action_cable_meta_tag %>` in your `views/layouts/application.html.erb`

Then let's adapt the view to work with Reflex, first modification is the `form` and `Tasks` title

```erb
<h2 class="text-lg mb-3">Tasks (<%= @project.tasks.count %>)</h2>
<%= form_with(
      model: [@project, @task],
      html: { data: { reflex: "submit->Task#create" }}) do |form| %>
```

We added a `@project.tasks.count` with the purpose to see how the count is changing in real-time while we add a new task, and the form has an `html: { data: { reflex: "submit->Task#create" }}` with this, we are telling it that when we submit the form, it will execute the `TaskReflex` and the `create` action but before add the Reflex class we need to add the Tasks list after the form.

```erb
<div class="mt-5">
  <% if @project.tasks.any? %>
    <ul>
      <% @project.tasks.each do |task| %>
        <li class="border-2 bg-gray-200 hover:bg-gray-300 rounded-lg p-2 mb-2">
          <%= task.content %>
        </li>
      <% end %>
    </ul>
  <% else %>
    <h3 class="text-md">Add your first task ☝️</h3>
  <% end %>
</div>
```

and finally the `TaskReflex`

```ruby
class TaskReflex < ApplicationReflex
  delegate :current_user, to: :connection

  def create
    @project = current_user.projects.find(params[:id])
    @project.tasks.create(task_params)
  end

  private

  def task_params
    params.require(:task).permit(:content)
  end
end
```

Notice that we have a `delegate :current_user, to: :connection`, this provide us a global user context, so for devise is necessary add this into `app/channels/application_cable/connection.rb`

```ruby
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user
      if (current_user = env["warden"].user)
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end
```

and that's it 🤟🏼

![Reflex is working!](https://res.cloudinary.com/codeando/image/upload/v1614142752/reflex-working_my3d91.gif)

You can see how the Tasks number is increasing while the task item is adding, and this, without reloading the page.

### Bonus

If you are an observer, you noticed that the input is not cleaned after the task is created, we can do it with `Stimulus`, yes, the Reflexes can be executed by Stimulus Controllers as well.

First step is modify the `form`, adding `data-controller`, `data-target` and `data-action`

```erb
<%= form_with(
      model: [@project, @task],
      html: { data: { controller: 'task' }}) do |form| %>
  <div class="flex mb-3">
    <%= form.text_field :content, data: { target: 'task.input' }, class: 'w-full rounded-lg mr-2', placeholder: 'Add a task' %>
    <%= form.button 'Create', data: { action: 'click->task#create' }, class: 'btn btn-primary' %>
  </div>
<% end %>
```

and finally let´s create a `Stimulus` controller called `task_controller.js` into `app/javascript/controllers/` with this

```js
import ApplicationController from './application_controller'

export default class extends ApplicationController {
  static targets = ['input']

  create(e) {
    e.preventDefault()

    this.stimulate('TaskReflex#create').then(() => {
      this.element.reset()
      this.inputTarget.focus()
    })
  }
}
```

The `data-controller` is the name of my file controller in this case `task`, `data-target` is the input and it's necessary to do the focus after the task is created end finally `data-action` tells us that we want to execute the create method when we click the button, so this method calls the Reflex action, the task is created, the input is cleaned and focused.

![Reflex completed!](https://res.cloudinary.com/codeando/image/upload/v1614144753/reflex-completed_d5sjzu.gif)

Now you can do `Reactive` applications with `Ruby on Rails` and `Stimulus Reflex` 💪

**You can read the original post on [codeando.dev](https://codeando.dev/posts/reactivity-rails-reflex/)**