第14章 Frames の失敗パターンと設計判断
この章のねらい
第11章から第13章で、Turbo Frames は強力だとわかりました。インライン編集も、遅延読み込みも、サイドバーも作れます。frame は入れ子にもできます。
しかし、強力だからこそ、使いすぎると複雑になります。この章では、「frame を使いすぎたときに現れる兆候」と、「そこで Turbo Streams や通常遷移へ切り替える判断」を学びます。これは第4部の締めであり、第5部 Turbo Streams への橋です。
この章では新しい機能は増やしません。代わりに、ここまで作ったものを振り返り、「どこまで frame でやるべきか」という設計判断の軸を持ちます。
14.1 frame の入れ子が深くなる兆候
第13章で、frame は入れ子にできると見ました。サイドバーの detail frame の中に、インライン編集の task_1 frame が入る、という形です。
入れ子は便利ですが、深くなると、自分でも追えなくなります。「このリンクをクリックすると、どの frame が差し替わるのか」が、すぐに答えられなくなったら危険信号です。
frame 内のリンクは、既定でいちばん内側の frame を差し替えます。data-turbo-frame で対象を変えれば、外側や別の frame も差し替えられます。入れ子が深いほど、この「どこが差し替わるか」の組み合わせが増え、予想とずれます。
対策は、入れ子を浅く保つこと、id を分かりやすく名づけること、そして「このリンクはこの frame」と説明できる範囲に留めることです。説明できなくなったら、設計を見直す合図です。
14.2 URL と画面状態のズレ
第11章で見たとおり、frame 内のリンクで遷移しても、ブラウザの URL は変わりません。これは部分更新の利点ですが、行きすぎると問題になります。
たとえば、サイドバーであるタスクの詳細を開いた状態で、ページをリロードしたとします。URL は一覧のままなので、リロードすると詳細は閉じ、最初の状態に戻ります。URL を誰かに共有しても、相手の画面に同じ詳細は出ません。戻るボタンの挙動も、ユーザーの期待とずれがちです。
つまり、いま見えている画面の状態が、URL から復元できない状態です。これは、frame で作り込むほど起きやすくなります。
その画面の状態を URL に残したいときは、frame の遷移を「URL も進める visit」に格上げします。リンクに data-turbo-action="advance" を付けます。
<%= link_to task.title, task_path(task),
data: { turbo_frame: "detail", turbo_action: "advance" } %>
こうすると、frame を差し替えつつ、ブラウザの URL も更新されます。リロードや共有に耐える画面になります。逆に言えば、「URL に残すべき主要な画面か、残さなくてよい補助的な部分か」を意識して、frame を使い分ける必要があります。
14.3 controller が frame 分岐だらけになる兆候
frame を増やすと、controller 側にも影響が出ます。「通常のリクエストのときはページ全体、frame からのリクエストのときは frame の中身だけ」と返し分けたくなり、リクエストの種類で分岐するコードが増えていきます。
controller のアクションが、リクエストが frame からかどうかで if 分岐だらけになってきたら、それは兆候です。多くの場合、同じビューの中に frame を置いておけば、Turbo が必要な frame だけを取り出してくれます。まずは、リクエストの種類で分岐を増やさずに済まないかを考えます。
分岐が増えてきたら、まず「同じビューを返して Turbo に任せられないか」を考えます。それでも複雑なら、次の 2 つの判断に進みます。
14.4 Streams へ切り替える判断
frame には、はっきりした限界があります。1 回の frame navigation で Turbo が差し替える対象は、1 つの frame だけです(frame の中にある複数の要素は、その frame ごとまとめて差し替わります)。
たとえば、タスクを 1 件削除したときに、離れた場所を同時に更新したいとします。
- 一覧から、その行を消す
- 「残り 12 件」という件数表示を更新する
- 「削除しました」というフラッシュを出す
これは、互いに離れた 3 か所の更新です。1 回の操作で差し替えられる frame は 1 つだけなので、frame だけではこの要求に応えられません。ここが、Turbo Frames から Turbo Streams へ切り替える判断点です。
第7部で使う判断軸を先取りすると、「更新する場所が 1 か所なら Turbo Frames、複数を同時に更新するなら Turbo Streams」です。複数箇所の同時更新が必要になったら、frame で粘らず、Streams へ移ります。これが、次の第5部のテーマです。
14.5 通常遷移に戻す判断
もう 1 つの判断は、逆方向です。そもそも部分更新が要らないなら、frame をやめて通常の遷移に戻す、という判断です。
frame は、「ページの一部を、周りと独立して差し替えたい」ときに価値があります。逆に、ページ全体が変わるような画面遷移(一覧から詳細へ完全に移動する、など)では、frame の独立性は要りません。それなのに frame で作ると、14.2 の URL のズレや、14.1 の入れ子の複雑さだけを抱え込みます。
「URL の問題に悩んでいる」「frame の入れ子と格闘している」けれど、その画面は本当は普通のページでよかった、という場合があります。そのときは、frame を外し、第3部で見た Turbo Drive の通常遷移に戻すのが正解です。Turbo Drive なら、ページ遷移は十分速く、URL も素直に変わります。
判断の基準は、「できるか」ではなく「読みやすく保てるか」です。frame でできるとしても、通常遷移の方が素直なら、そちらを選びます。
第4部では、Turbo Frames を「独立した小さな visit 領域」として学び、その使いどころと、使いすぎの見切りまで見ました。14.4 で触れたとおり、複数箇所を同時に更新したくなったら、次の道具が要ります。第5部では、その Turbo Streams を学びます。
参考資料
- Turbo Frames(Handbook): https://turbo.hotwired.dev/handbook/frames
- Turbo Drive(Handbook): https://turbo.hotwired.dev/handbook/drive
- Turbo の属性リファレンス: https://turbo.hotwired.dev/reference/attributes