はじめに
私のとあるRailsプロジェクトは Hotwire Stimulus ✕ React 構成を採用していた。グラフ描画の機能を実装するために流行りの tremor を使ってみたかったからである。
実装
実装は以下の通り。
app/javascript/components/RevisionFluctuation.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { Card, Title, LineChart } from "@tremor/react";
const RevisionFluctuation = ({ data }) => (
<>
<Card>
<Title>Revision fluctuation</Title>
<LineChart
data={data}
dataKey="date"
categories={["Revision count"]}
colors={["orange"]}
marginTop="mt-6"
yAxisWidth="w-10"
/>
</Card>
</>
);
RevisionFluctuation.defaultProps = {
data: [],
container: HTMLDivElement,
};
const renderRevisionFluctuation = (
data: { date: string; "Revision count": number }[],
container: HTMLDivElement,
) =>
container &&
createRoot(container).render(<RevisionFluctuation data={data} />);
export default renderRevisionFluctuation;
app/javascript/controllers/user_info_controller.ts
import { Controller } from "@hotwired/stimulus";
import renderRevisionFluctuation from "../components/RevisionFluctuation";
// Connects to data-controller="user-info"
export default class extends Controller {
static targets = ["revise-dates"];
static values = {
revise_dates: Object,
};
declare readonly reviseDatesValue: { [key: string]: number };
connect() {
const revisionsPerDateCount = Object.entries<number>(
this.reviseDatesValue,
).map(([date, count]) => {
return { date: date, "Revision count": count };
});
document.addEventListener("turbo:load", () =>
renderRevisionFluctuation(
revisionsPerDateCount,
this["revise-datesTarget"],
),
);
}
}
なにが悪いか
結局のところ、data-user-info-value
といった形でフロントエンドのERBから値を直に渡さなくてはならないことが悪いのである。値を渡すために Stimulus コントローラを作成して、JSX を書き、Rails のコントローラとビューを書き換える必要がある。値がフロントエンドで丸見えになることもよくない。
そんなわけで Hotwire Stimulus ✕ React 構成を取るくらいなら、フロントエンドを脱 Rails 化した方がマシだ!という結論に至るのである。