Hotwireにはいくつかの機能がありますが、まずはとても効果がわかりやすいところとしてTurbo FramesでHTMLを部分更新できるので、これを見てみましょう。
- Railsでの用例の話をします
- 記事の読者はRailsの基礎は知っているものとします
- Rails7.0.4 + Ruby3.1.2 で動作させています
おおまかな流れ
まずは概要を頭に入れて、流れをイメージしやすくしましょう。
以下は、
- 基本的なRailsアプリ
- Rails+JS(Reactなど)アプリ
- Rails+Turbo Framesアプリ
の3つの、処理の違いをシーケンス図に表したものです。(PlantUMLで書きました)
Turbo Framesの重要なポイントは
- 基本的なRailsアプリと違い、HTMLの必要な部分だけを変更するため、ページ全体を切り替えるよりブラウザの処理が軽くなる
- Rails+JS(Reactなど)アプリと違い、Rails server側でHTMLを作るため、JSの記述をとても少なくできる(基本的な機能だけならゼロにできる)
- HTMLを作るのはRails側!というまとまった状態を維持できる、という事でもあります
というところだと思います。
実装
部分更新をする例です。「おみくじ機能があるダッシュボード」を考えてみます。
class DashboardController < ApplicationController
# root "dashboard#index"
def index
end
# post 'fortune', to: 'dashboard#fortune'
def fortune
@fortune = %w(大吉 中吉 小吉 凶).sample
render partial: 'fortune'
end
end
<p>こんにちは</p>
<%= turbo_frame_tag 'fortune' do %>
<%= form_with url: fortune_path do |f| %>
<%= f.submit 'おみくじを引く!' %>
<% end %>
<% end %>
<%= turbo_frame_tag 'fortune' do %>
<p>今日の運勢は<%= @fortune %>です</p>
<%= form_with url: fortune_path do |f| %>
<%= f.submit '引き直す!' %>
<% end %>
<% end %>
rails newしてこれらのコード(とroutes.rb)を書くだけでTurbo Framesを早速動作させることができます。
ここで重要な事は2つ、
- turbo_frame_tag に引数で渡した名前が一致する箇所を部分更新する
- controller上で
render partial: 'xxx'
と書くとpartialの内容だけをレスポンスに返す
です。
動作
これが、
こうなります。
例がシンプルすぎて「普通じゃん」ってなってしまいそうなので、何が起きているのかもう少し見ていきましょう。
最初のページのHTMLは以下のようになっています。
<!DOCTYPE html>
<html>
<head>
(省略)
</head>
<body>
<p>こんにちは</p>
<turbo-frame id="fortune">
<form action="/fortune" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="U2VGLjfze2oiCQI5M6dDbGV4SoOZ7UPVg1QILztHNFd7pL51Pg8VnAFcbL5F_cyeitHzT_tmIu55eZW_66fvBg" autocomplete="off" />
<input type="submit" name="commit" value="おみくじを引く!" data-disable-with="おみくじを引く!" />
</form>
</turbo-frame>
</body>
</html>
erbファイルにturbo_frame_tagを書いた部分は、HTML上ではturbo-frameタグになっています。
次に、おみくじを引くリクエストのレスポンスのHTMLは以下のようになっています。
<turbo-frame id="fortune">
<p>今日の運勢は凶です</p>
<form action="/fortune" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="tQdPBfoZP7j2pXfTEM7_njNTeaFxFNriKhqVFhpgNr2dxrde8-VRTtXwGVRmlHBs3PrAbROfu9nQNwiGyoDt7A" autocomplete="off" />
<input type="submit" name="commit" value="引き直す!" data-disable-with="引き直す!" />
</form>
</turbo-frame>
抜粋したのではなくて、これで全部です。
turbo-frameタグのidが同じで、Turbo Framesがこの内容を見て、元のページのturbo-frameタグの内容を置き換えます。
それでおみくじの結果が画面に出る、という流れです。
ここまで見ておわかりかと思いますが、Turbo Framesは「こういうHTMLを返してくれるサーバーだったらRailsじゃなくても別に良い」んですよね。
独立したJSプロダクトで、Railsはあくまで連携するためのサポート機能を持っているだけです。
もっと便利に使う
ここまでの例は、基本の原理がわかりやすいように最初のページとおみくじの結果を分けていました。
実際にアプリを作るときはもっと便利に書けるので、それもやってみましょう。
erbファイルを修正します。
<p>こんにちは</p>
<%= render partial: 'fortune' %>
<%= turbo_frame_tag 'fortune' do %>
<% if @fortune %>
<p>今日の運勢は<%= @fortune %>です</p>
<%= form_with url: fortune_path do |f| %>
<%= f.submit '引き直す!' %>
<% end %>
<% else %>
<%= form_with url: fortune_path do |f| %>
<%= f.submit 'おみくじを引く!' %>
<% end %>
<% end %>
<% end %>
このように、最初のページと更新時で同じpartialを使うように書けます。
もっとシンプルに、最初のページを開いたときにおみくじの結果を表示して良ければ、
class DashboardController < ApplicationController
# root "dashboard#index"
def index
@fortune = %w(大吉 中吉 小吉 凶).sample
end
# post 'fortune', to: 'dashboard#fortune'
def fortune
@fortune = %w(大吉 中吉 小吉 凶).sample
render partial: 'fortune'
end
end
<%= turbo_frame_tag 'fortune' do %>
<p>今日の運勢は<%= @fortune %>です</p>
<%= form_with url: fortune_path do |f| %>
<%= f.submit '引き直す!' %>
<% end %>
<% end %>
と、partialの内容を全く同じにしてしまう事もできます。ここまで来ると、RailsとTurbo Framesの連携がなかなか衝撃的に感じられるんじゃないでしょうか。
終わり
Hotwireの便利さを体感できるシンプルな例を出してみました。私もまだ触り始めたばかりで、色々と資料をあたって断片的な情報を結びつけてここに至った感じです。基本的な仕組みがわかりやすいシンプルな例があると良いなと思ったのでまとめてみました。
次はTurbo Streamsについて書きます書きました。