考える手順
Hotwireは大きくTurbo, Stimulus, Nativeの技術から構成されています。そしてTurboはさらにTurbo Drive, Turbo Frames, Turbo Streamsに分かれています。Nativeはモバイルアプリを作るためのツールなので使い分けが明確です。一方でTurboとStimulusのそれぞれの役割と使い分け方は迷うかもしれません。
ここではTurboとStimulusの使い分けについて解説します。
TurboとStimulusの役割を知るには、インタラクティブUIの要件を定義する必要があります。明確な定義はありませんが、インタラクティブなUIは一般的に以下の動作をするものと言えます。
つまり 「イベント ==> 画面更新」 までを同じページの中で処理するのがインタラクティブUIの要件と言えます
こう考えると、「イベント ==> 画面更新」 までの流れの中でTurboとStimulusの役割を考えることができます。
action
でイベントをハンドルし、さらに画面のtarget
で指定した箇所を更新します<a>
タグのclick
イベント、<form>
タグのsubmit
イベントなど、ブラウザのネイティブなイベントを真似、これらのイベントを自動的に処理します。サーバからレスポンスを受けると、Turbo Drive, Turbo Frames, Turbo Streamsのそれぞれ決まった方法に従って、レスポンス内容を画面に反映します。Turbo Driveの場合は画面遷移、Turbo Framesの場合は画面の一箇所の部分置換、Turbo Streamsの場合はレスポンス次第で複数箇所の部分置換や部分追加、削除を行います<a>
タグのclick
イベント、<form>
タグのsubmit
イベント以外のイベントを処理したい場合は、Stimulusでイベントをハンドルして、サーバにTurboのリクエストを送信します。例えばライブ検索であれば<input>
タグのinput
イベントに反応したり、キーボードショートカットであればkeydown
イベントに反応できるようになります。Turboのリクエストを送信することで、上述したTurbo Drive, Turbo Frames, Turbo Streamsによる自動処理が行われます。<form>
からPOSTのリクエストを受取、成功した場合にモーダルダイアログを自動的に閉じるなどの処理が可能になります。このように、Hotwireではブラウザネイティブ、Stimulus、Turboのイベント処理を組み合わせて、望み通りのUI/UXを実現します。サーバとの通信が不要な場合はStimulusだけで処理し、サーバとの通信が必要な場合は主にTurboを使いながら、補佐的にStimulusを使います。
HotwireのJavaScriptを書いていると、async awaitをほとんど書かないことに気づきます。それどころか、Hotwireで使っているJavaScriptは入門者レベルの簡素なものばかりです。
この大きな要因はHotwireがJavaScriptイベントベースだからです。Stimulusがアクションを受け取るところ、Turboがレスポンスに対して後処理をする箇所はすべてイベントベースです。イベントはすべて非同期(async)に実行されます。Stimulus Controllerの中でasyncなコードを書かなくても、JavaScriptがそれをすべて非同期処理してくれるのです。
一方でReactは主にイベントではなくコールバックのパラダイムで記述します。ボタンをクリックした時のイベントハンドラはコールバック(関数へのレファランス)として親コンポーネントから子コンポーネントに渡されます。useEffect
の中身もコールバックとして記述します。
イベントベースの記述とコールバック中心の記述は本質的な違いはないのですが、人間にとってのわかりやすさには大きな違いはあります。特に初心者や難しいJavaScriptをあまり書かない人にとってはイベントベースの方がわかりやすいです。
上記ではモーダルダイアログのUIを想定しています。そしてモーダルの中身はサーバから取得するものとします。
useEffect()
が起動し、fetch()
でサーバにリクエストを送信しますこのようにReactでは必ず最初にステートを更新し、ステートから自動的に画面を更新します。インタラクティブUIの流れを敢えて一種類に絞っています。一種類しかないのでわかりやすい反面、自動処理をしてくれる箇所がなく、すべてカスタムで用意することになります。
上述のように、Turboはサーバとの通信が必要な時だけに使用します。ブラウザがすでに持っているデータだけで完了する場合はTurboは不要で、Stimulusだけで十分です。しかしこの判断は意外と単純ではありません。下記のことを考慮して最終判断をする必要があります。
上述のように、TurboとStimulusのどれを使うかは単純ではありません。開発の初期から考えすぎないことも重要です。私は通常、下記の順番で開発しています。
インタラクティブUI/UXは最適化と捉えるのが良いと思います。時期尚早な最適化を避ける意味でもMPAから出発し、段階的にインタラクティブな要素を追加するのが良いのではないかと思います。