Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
2
Help us understand the problem. What are the problem?

posted at

updated at

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

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

Hotwire(Turbo)とは

Hotwireは、Railsの作者であるBasecampのDHHが昨年発表したものです。「モダンなウェブアプリケーションを、たくさんのJavaScriptなしで、JSONの代わりにHTMLを送って作るもの」だそうです。

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

  • Turbo
  • Stimulus
  • Strada

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

参考にしたサイト

Hotwireのサイトの他に:

Hotwire (Turbo)の導入

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

% rails new hotwire-sample
% cd hotwire-sample

Gemfileにhotwire-railsを加えます。

Gemfile
gem 'hotwire-rails'

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

% bundle install

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

% bin/rails hotwire:install

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

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

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

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」のサンプルではActionCableもRedisも要りません。

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

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

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-trackdata-turbo-trackに変える必要がありますが、ここは自動的に修正されないので手動で直します。

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-railsstimulus-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

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

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

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

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を完全に切る年になるか。

続き:

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
2
Help us understand the problem. What are the problem?