Hotwire（Turbo）を試す その1: 導入、作成・更新フォーム

環境: Rails 6.1、Turbo 7.0.0-beta.5

## Hotwire（Turbo）とは

[Hotwire](https://hotwire.dev/)は、Railsの作者であるBasecampのDHHが[昨年発表した](https://twitter.com/dhh/status/1341420143239450624)ものです。「モダンなウェブアプリケーションを、たくさんのJavaScriptなしで、JSONの代わりにHTMLを送って作るもの」だそうです。

Hotwireは次の3つからなります。

- Turbo
- Stimulus
- Strada

といっても、Stimulusは前からありますし、Stradaはまだできていないので、実際に試してみるのはもっぱらTurboということになります。Turboは、簡単に言えば今までのTurbolinksを発展させたものです。「Turbolinksがなんかすごくなったもの」です。

## 参考にしたサイト

[Hotwire](https://hotwire.dev/)のサイトの他に:

- [Hotwire: Reactive Rails with no JavaScript? — Martian Chronicles, Evil Martians’ team blog](https://evilmartians.com/chronicles/hotwire-reactive-rails-with-no-javascript) : by [Vladimir Dementyev](https://twitter.com/palkan_tula) 2021-04-12 
- [How to use Hotwire in Rails (Example) | GoRails](https://gorails.com/episodes/hotwire-rails) : by [Chris Oliver](https://twitter.com/excid3) 2020-12-23
- [turbo-railsのソースコード](https://github.com/hotwired/turbo-rails) : app/ の下あたり

## Hotwire （Turbo）の導入

では試してみましょう。Rails 6.1でアプリケーションを作ります。

```
% rails new hotwire-sample
% cd hotwire-sample
```

Gemfileに[hotwire-rails](https://github.com/hotwired/hotwire-rails)を加えます。

```ruby:Gemfile
gem 'hotwire-rails'
```

Gemをインストールします。

```
% bundle install
```

次のコマンドを実行すると、RailsアプリケーションにTurboとStimulusが入ります。

```
% bin/rails hotwire:install
```

Gemfileが自動的に修正され、Turbolinksが外されてRedisが必須になります。もう一度 `bundle install` を実行します。

```diff:Gemfile
- gem 'turbolinks', '~> 5'
+
- # gem 'redis', '~> 4.0'
+ gem 'redis', '~> 4.0'
```

package.jsonでは、Turbolinksの代わりにTurboが入ります。

```diff:package.json
  "dependencies": {
+   "@hotwired/turbo-rails": "^7.0.0-beta.5",
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "5.2.1",
-   "turbolinks": "^5.2.0"
+   "stimulus": "^2.0.0"
  },
```

cable.ymlが修正されています。ActionCableを使うときは開発環境でRedisが必要なようです。なお、Turboを使うにはActionCableが必須というわけではありません。この「その1」と「[その2](https://qiita.com/kazutosato/items/74ab0a22d41cd8859fad)」のサンプルではActionCableもRedisも要りません。

```diff:config/cable.yml
development:
-  adapter: async
+  adapter: redis
+  url: redis://localhost:6379/1
```

application.jsでも、Turbolinksの代わりにTurboが入り、Stimulusの初期化（`import "controllers"`）が加わります。

```diff:app/javascript/packs/application.js
import Rails from "@rails/ujs"
- import Turbolinks from "turbolinks"
+ import "@hotwired/turbo-rails"
import * as ActiveStorage from "@rails/activestorage"
import "channels"

Rails.start()
- Turbolinks.start()
ActiveStorage.start() 
+ import "controllers"
```

レイアウトテンプレートで`data-turbolinks-track`を`data-turbo-track`に変える必要がありますが、ここは自動的に修正されないので手動で直します。

```erb:app/views/layouts/applicaton.html.erb
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbo-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbo-track': 'reload' %>
```

なお、hotwire-railsは[turbo-rails](https://github.com/hotwired/turbo-rails)と[stimulus-rails](https://github.com/hotwired/stimulus-rails)をまとめたものです。Gemfileにhotwire-railsの代わりに`gem 'turbo-rails'` を加えて、次のコマンドを実行しても同じです。`webpacker:install:stimulus` を実行しなければ、StimulusなしでTurboだけ使えます。

```
% bundle install
% bin/rails turbo:install
% bin/rails webpacker:install:stimulus
```

## 作成・更新フォーム

Turboの機能を調べるために、作成・更新フォームを作ってみます。scaffoldで手抜きします。

```
% bin/rails g scaffold entry title:string body:text
% bin/rails db:migrate
```

エラー表示を試すために、バリデーションを加えます。

```ruby:app/models/entry.rb
class Entry < ApplicationRecord
  validates :title, :body, presence: true
end
```

`http://localhost:3000/entries` から新規作成と編集画面を試すと、Turboのために何も手を加えなくても、そのまま動作します。

バリデーションエラーのときの挙動は、これまでのRailsとは変わります。POSTでブラウザーが画面遷移せずに、TurboのJavaScriptが画面を差し替えます。URLは /entries/new や /entries/:id/edit のままです。

![turbo1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308595/29b4bf55-bb34-0acb-0326-4caa654ec642.png)

注意しなければならないのは、Turboを使っているときにPOST（PATCH、DELETE）メソッドでリクエストを送ったら、リダイレクトするかHTTPステータスをエラーにする（300番台か400番台のステータスを返す）必要があることです。200を返すと画面が変わりません。

```ruby:app/controllers/entries_controller.rb
  def create
    @entry = Entry.new(entry_params)
    if @entry.save
      redirect_to @entry, notice: "Entry was successfully created."
    else
      render :new, status: :unprocessable_entity # statusが必須
    end
  end
```

POSTのときにバグによる500エラーが出たときは、TurboはRailsのいつものエラー画面を出してくれません。これはそういうものなのか、これから変わるのか分かりません。

## IE対応

TurboはIE 11で動きません。Polyfillをあれこれ入れて試してみましたが、無理そうです。

## ここまでの感想

- Turboは、Turbolinksよりもめっちゃ速い。
- バリデーションエラーでPOSTによる画面遷移をしないのは、たいへんありがたい。このためだけでもTurboを使いたい。
- 今年はいよいよIEを完全に切る年になるか。

続き:

- [Hotwire（Turbo）を試す その2: Trubo Streamsでページの一部の差替・追加](https://qiita.com/kazutosato/items/74ab0a22d41cd8859fad)
- [Hotwire（Turbo）を試す その3: Trubo Streamsでのブロードキャスト](https://qiita.com/kazutosato/items/4b2d4177fc92335536e9)