React脳からRails Hotwireを触るときは思考転換が必要

に公開
  • 最近Railsを学び直している。単体でなんとかしたい、という気持ち。One Person Framework.
  • Reactから来たひとがHotwireを使うならJSON操作脳から思考の転換をする必要がある
  • React(SPA+API)開発とRails(のみ)開発の根本的な発想の違いを抑える。

考え方の違い

  • React+APIの考え方
    • クライアント側からAPIを叩いてJSONデータを取得する
    • 取得したJSONを元にクライアントJSが選択肢やデータを組み立てて表示する
  • Rails Hotwireの考え方
    • データの操作は基本的にHTTPアクションを通じてのみ行う
    • HTMLとHTTPでの操作が正である
    • サーバー側が構築したHTMLの断片を受け取りDOMを差し替える
    • StimulusはHTMLのちょっとした操作を拡張するためのものとして捉える

具体例の比較

  • 要件としてselectで選んだデータに紐づく詳細を下に表示するUIを作る

Reactの発想

  • GETリクエストでJSONを取得してJSで組み立てる
const handleChange = async (e) => {
  const res = await fetch(`/api/items/${e.target.value}`);
  const json = await res.json();
  setSelectedItem(json);
};

return (
  <select onChange={handleChange}>
    <option value="1">Item 1</option>
  </select>
  <Detail data={selectedItem} />
);

Rails Hotwireの発想

  • selectのchangeをStimulusが受け取る
  • StimulusがHTTP POSTでsubmitを実行する
  • サーバー側がパーシャルからHTMLを生成する
  • Hotwire StreamsがHTMLを差し替える
<form data-controller="auto-submit" action="/items/select" method="post">
  <select name="item_id" data-action="change->auto-submit#submit">
    <option value="1">Item 1</option>
    <option value="2">Item 2</option>
  </select>
</form>

<div id="detail-area">
    最初の状態だよ〜差し替えられるよ〜
</div>
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  submit() {
    this.element.requestSubmit()
  }
}
class ItemsController < ApplicationController
  def select
    @item = Item.find(params[:item_id])
    respond_to do |format|
      format.turbo_stream
    end
  end
end
<%= turbo_stream.update "detail-area" do %>
  <div><%= @item.name %></div>
  <div><%= @item.description %></div>
<% end %>

結論

  • ReactのGETでJSONを取ってきてJSで組み立てる発想を捨てる
  • サーバーにリクエストを送りサーバーが返したHTMLでDOMを置き換える前提を持つ
  • StimulusはHTMLの延長で、HTTPリクエストを発生させるトリガーとして使う。単体でAPIを叩いてDOM変えるなどしない。

Discussion