はじめに
Webサイトを見ていると、ページを切り替えなくても新しい情報がリアルタイムで表示されたり、ボタンを押した瞬間に画面の一部がサッと変わったりすることがあります。このような快適なUIを、簡単に作れるのが、RailsのHotwireです。
Hotwireは、主にTurboとStimulusという2つの主要なライブラリで構成されています。 Turboは、サーバーが生成したHTMLを使ってページの高速なナビゲーションや部分的な更新を行う一方、Stimulusは最小限のJavaScriptでHTMLに振る舞いを追加する役割を担います。
このTurboに含まれる turbo_stream は、サーバーからの命令に応じてDOMを直接操作するためのものです。しかし、turbo_streamには似たようなアクションが複数あり、その使い分けに迷うことがあります。
この記事では、turbo_streamの7つの主要アクションについて、それぞれの役割と使い分けを、自分なりにまとめてみました。
turbo_streamとは?
従来のWeb開発では、UIの特定の部分を更新する際、JavaScriptを使ってAPIからデータを受け取り、DOM(Webページの構造)を操作する必要がありました。
turbo_streamは、このアプローチを根本から変えます。サーバーは、HTMLの断片と、それをどこに、どのように挿入するかという「命令」をWebSocket経由でクライアントに送信します。クライアント側のTurboフレームワークがその命令を受け取り、DOMを自動で更新してくれるのです。
つまり、turbo_streamの各アクションは、「サーバーがクライアントのDOMに実行させたい命令」だと考えると、理解しやすくなります。
turbo_streamの各アクションについて
動的にHTMLを追加・操作する入れ物となるHTMLの基本形は以下のようになります。このidが、turbo_streamのtarget属性で指定する対象となります。
<div id="comments">
</div>
1.リストに追加するアクション (append, prepend)
この2つのアクションは、指定した要素の内部に新しいHTMLを追加したいときに使います。
append: 新しいコンテンツを、指定した要素の末尾に追加します。
【追加前】
<div id="comments">
<div id="comment1">
<p>コメント1</p>
</div>
</div>
【turbo_streamコード】
<turbo-stream action="append" target="comments">
<template>
<div id="comment2">
<p>コメント2</p>
</div>
</template>
</turbo-stream>
【追加後】
<div id="comments">
<div id="comment1">
<p>コメント1</p>
</div>
<div id="comment2">
<p>コメント2</p>
</div>
</div>
prepend: 新しいコンテンツを、指定した要素の先頭に追加します。
【追加前】
<div id="comments">
<div id="comment1">
<p>コメント1</p>
</div>
</div>
【turbo_streamコード】
<turbo-stream action="prepend" target="comments">
<template>
<div id="comment2">
<p>コメント2</p>
</div>
</template>
</turbo-stream>
【追加後】
<div id="comments">
<div id="comment2">
<p>コメント2</p>
</div>
<div id="comment1">
<p>コメント1</p>
</div>
</div>
2. 要素を置き換えるアクション (replace, update)
この2つのアクションは、既存のHTMLを新しいHTMLに置き換えたいときに使います。
replace: 指定した要素自体を、新しいHTMLに完全に置き換えます。
【置き換え前】
<button id="like-button-123">いいね!</button>
【turbo_streamコード】
<turbo-stream action="replace" target="like-button-123">
<template>
<button id="like-button-123">いいね済み</button>
</template>
</turbo-stream>
【置き換え後】
<button id="like-button-123">いいね済み</button>
update: 指定した要素の内部のコンテンツのみを新しいHTMLに置き換えます。
【更新前】
<div id="like-count">
<span>いいね数: </span>
<span id="count">9</span>
</div>
【turbo_streamコード】
<turbo-stream action="update" target="count">
<template>
<span>10</span>
</template>
</turbo-stream>
【更新後】
<div id="like-count">
<span>いいね数: </span>
<span id="count">10</span>
</div>
3. 要素を挿入・削除するアクション (before, after, remove)
これらのアクションは、指定した要素の外側にHTMLを挿入したり、要素を削除したりする場合に使います。
before: 指定した要素の直前に新しいHTMLを挿入します。
【挿入前】
<div id="item-list">
<div id="item2">項目2</div>
</div>
【turbo_streamコード】
<turbo-stream action="before" target="item2">
<template>
<div id="item1">項目1</div>
</template>
</turbo-stream>
【挿入後】
<div id="item-list">
<div id="item1">項目1</div>
<div id="item2">項目2</div>
</div>
after: 指定した要素の直後に新しいHTMLを挿入します。
【挿入前】
<div id="item-list">
<div id="item1">項目1</div>
</div>
【turbo_streamコード】
<turbo-stream action="after" target="item1">
<template>
<div id="item2">項目2</div>
</template>
</turbo-stream>
【挿入後】
<div id="item-list">
<div id="item1">項目1</div>
<div id="item2">項目2</div>
</div>
remove: 指定した要素をDOMから完全に削除します。
【削除前】
<div id="comment-list">
<div id="comment1">コメント1</div>
<div id="comment2">コメント2</div>
</div>
【turbo_streamコード】
<turbo-stream action="remove" target="comment2"></turbo-stream>
【削除後】
<div id="comment-list">
<div id="comment1">コメント1</div>
</div>
まとめ:アクションの使い分け
| アクション | 役割 | 置き換えの範囲 | 具体例 |
|---|---|---|---|
| append | 末尾に追加 | 要素の内部 | リストに新しい項目を追加 |
| prepend | 先頭に追加 | 要素の内部 | 最新のコメントを一番上に表示 |
| replace | 要素ごと置き換え | 要素全体 | いいねボタンの切り替え |
| update | 中身だけ置き換え | 要素の内部 | いいね数の更新 |
| before | 手前に挿入 | 要素の外側 | 項目間の区切り線を追加 |
| after | 後ろに挿入 | 要素の外側 | 関連項目リストを挿入 |
| remove | 削除 | 要素全体 | 項目を削除する |