はじめに
今回、新しいプロジェクトで、Ruby on Rails 7、React.jsによるフロントエンド開発に着手することになったので、開発手順の備忘録記載。
Railsは1年半ぶりくらいに触ることになり、いつの間にかVersion7になっていたことにビックリ。忘れている部分も多い。また、Reactは初めてなので、TypeScriptとともに0から学びながら開発していく。
jsbundling-rails
を使ったTypeScriptのセットアップ方法、Reactコンポーネントの作成と組み込み方法、そしてRailsのERBファイルでのコンポーネントの呼び出し方法について記載。
全体構成
RailsとReactの組み合わせによるアプリケーションの全体構成は、以下
- View (Actionごと): Reactコンポーネントを使用して、各画面ごとに独立したtsxファイルを作成し、RailsのViewがそれを呼び出す。
- Controller (Ruby): 各Actionに対応するRailsのコントローラーがあり、必要なデータを処理し、Reactコンポーネントに渡すためのデータを準備する。
- Model: データベースとやり取りを行い、ビジネスロジックを実行。
環境構築
JSアセットパイプライン:jsbundling-rails + esbuild の導入
Rails 7では、JavaScript (JS) と Cascading Style Sheets (CSS) のバンドル方法が変更された。従来のWebpackerに代わって、jsbundling-rails
とcssbundling-rails
が導入され、バンドル方法がよりシンプルかつ直感的になった。
jsbundling-rails
は、JavaScriptのバンドルに関する新しいアプローチで、esbuild
やrollup.js
、webpack
など、複数のバンドラーをサポートしている。
esbuild
は極めて高速なJavaScriptバンドラーおよびミニファイヤーで、大規模なJavaScriptコードベースでも素早くバンドルすることができる。
Railsプロジェクトにjsbundling-rails
とesbuild
を導入するには、以下の手順を実行。
jsbundling-rails
をGemfileに追加してインストール:
# Gemfile
gem 'jsbundling-rails'
その後、bundle install
を実行。
esbuild
をセットアップ:
bin/rails javascript:install:esbuild
これにより、esbuild
がセットアップされ、app/javascript
ディレクトリ内で利用可能になる。
TypeScriptのセットアップ
RailsプロジェクトにTypeScriptを導入するためには、まず以下のコマンドを実行して必要なパッケージをインストールする。
yarn add typescript @types/react @types/react-dom @babel/preset-typescript
このコマンドにより、TypeScriptとそのReact用の型定義、そしてBabelのTypeScriptプリセットがプロジェクトに追加される。
RailsでのPathとアクションの設定
まず、Railsで画面を描画するためのPathを作成する
Dockerコマンドを利用したコントローラーとアクションの自動生成
下記コマンドで新しいコントローラーとアクションを自動生成する。
$ docker-compose run --rm app bin/rails generate controller [コントローラー名] [アクション名]
例えば、「Users」というコントローラーに「show」というアクションを追加したい場合、次のようにコマンドを実行する。
$ docker-compose run --rm app bin/rails generate controller Users show
Creating app_run ... done
invoke rails
create app/controllers/users_controller.rb
route get 'users/show'
invoke erb
create app/views/users
create app/views/users/show.html.erb
invoke test_unit
create test/controllers/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
invoke assets
invoke scss
create app/assets/stylesheets/users.scss
これにより、app/controllers/users_controller.rb
にshow
メソッドが追加され、関連するViewファイルがapp/views/users
ディレクトリに作成される。
このとき、自動的にtestファイルやhelperファイルも作成される。もし不要であれば、削除する。
Routingの設定
Railsにおいて、RoutingはURLとコントローラーのアクションを結びつける役割を持つ。config/routes.rb
ファイルを編集することで、ルーティングを設定できる。
以下は、UsersController
のshow
アクションに対応するルートを定義する例。ここで:id
はパラメータとして、ユーザーのIDを動的に受け取ることを示している。
Rails.application.routes.draw do
get 'users/:id', to: 'users#show'
end
app/controllers/users_controller.rb
にshow
アクションを定義する。このアクションは、対応するビューをレンダリングするために使用される。
class UsersController < ApplicationController
def show
# ここに必要なロジックを追加
end
end
Railsの命名規則:snake_case
スネークケース (snake_case
)
- ファイル名、ディレクトリ名、変数名、関数名(またはメソッド名)で使用
- 単語はアンダースコアで区切られ、全て小文字で記述(例:
user_profile.rb
,calculate_age
)
コントローラー名やモデル名などは、単数形や複数形の使用がルールによって定義されており、それらは自動的に関連するファイル名やクラス名に反映される。例えば、UsersController
クラスはusers_controller.rb
に、User
モデルはuser.rb
に対応する。
TypeScriptで画面作成
Reactコンポーネントを作成
app/javascript/components/User.tsx
ファイルを作成し、以下のように記述する。
Reactでは、UIの一部を表すコンポーネントを作成するために関数またはクラスを使用する。この場合、User
は関数コンポーネントとして定義されており、特定のユーザーの名前(name
)と年齢(age
)を表示するためのもの。
関数コンポーネントは、propsとして受け取ったデータをもとにReactのElementを返す。このUser
コンポーネントはUserProps
型のpropsを引数に取り、name
とage
プロパティを使ってUIを構築し、それを返している。
ここでのUser
変数は、Reactのコンテキストでは「コンポーネント」として扱われる。これを他のReactコンポーネントから<User name="John Doe" age={30} />
のように使用することで、そのユーザー情報を表示することができる。
文字列などのプリミティブな値を直接渡す場合は引用符(""
)を使用。
JavaScriptの式や変数の値を渡す場合は、波括弧{}を使用してその式を囲む。
// app/javascript/components/User.tsx
import React from 'react';
type UserProps = {
name: string;
age: number;
};
// React.FCを使用せずにコンポーネントを定義
const User = ({ name, age }: UserProps) => {
return (
<div>
<h1>{name}</h1>
<p>{age} years old</p>
</div>
);
};
export default User;
TypeScriptとDartの共通点と違いに関する記事は、こちら。
ファイル名とコンポーネント名(const名)、export名に関して
ファイル内で記載するコンポーネント名(const)とexport名は一致していなければならない。
Show.tsx
ファイル内で const Show1
と定義し、export default Show2
とする場合、これは構文的に正しくないため、コンパイルエラーまたは実行時エラーになる。export default
でエクスポートする値は、そのファイル内で定義された変数、関数、クラスなどの実際のエンティティでなければならない。
Show.tsx
ファイル内で const Show1
と定義し、export default Show1
とする場合、これは構文的に正しいため、実行できる。
ただし、可読性・保守性の観点からファイル名とコンポーネント名(const名)を一致させることが推奨されている。
つまり、結論としては、ファイル名とコンポーネント名(const名)、export名はすべて一致させた方が良い。ちなみに、このコンポーネント名をどのような名前で使用するかもインポート先のファイル名で設定できる。
TypeScriptの命名規則:PascalCase・camelCase
パスカルケース(PascalCase
)
- すべての単語の最初の文字は大文字で始まる。例:
UserProfile
,UserInterface
。 - コンポーネントやクラスを定義するファイル名
- クラス名やインターフェイス名、コンポーネント名
キャメルケース(camelCase
)
- 最初の単語は小文字で始まり、後続の単語の最初の文字は大文字で始まる。例:
userName
,calculateAge
。 - ユーティリティファイルや単一関数を定義するファイル名
- 変数名や関数名
Userページの作成(ファイルパスと命名)
app/javascript/controllers/users/Show.tsx
ファイルを作成し、以下のように記述する。
ファイルのパスと命名:
app/javascript/controllers/users/Show.tsx
この構造では、app/javascript/controllers/
ディレクトリの下にバックエンドのコントローラー名に対応するディレクトリを作成し(この場合は users/
)、その中に対応するアクションメソッド名に基づいた .tsx
ファイルを作成している。
ここでは、UsersController
の show
アクションに対応して Show.tsx
というファイルを作成。
User
コンポーネントをUserという名前でインポートし、固定のデータを渡してユーザー情報を表示。
// app/javascript/controllers/users/Show.tsx
import React from 'react';
// Userコンポーネントのインポートパス。適宜調整
import User from '../components/User';
const Show = () => {
return (
<div>
<h1>User Information</h1>
<User name="John Doe" age={30} />
</div>
);
};
export default Show;
ちなみに、下記は正しく実行される。
コンポーネントファイルのUser.tsx
で User1
コンポーネントを定義し、デフォルトエクスポートし、別のファイルでこのコンポーネントを import User3 from '../components/User';
としてインポートしている。ただし、基本的には可読性の観点から、すべてUserで統一した方が良い。
// app/javascript/components/User.tsx
import React from 'react';
type UserProps = {
name: string;
age: number;
};
// React.FCを使用せずにコンポーネントを定義
const User1 = ({ name, age }: UserProps) => {
return (
<div>
<h1>{name}</h1>
<p>{age} years old</p>
</div>
);
};
export default User1;
// app/javascript/controllers/users/Show.tsx
import React from 'react';
// Userコンポーネントのインポートパス。適宜調整
import User3 from '../components/User';
const Show = () => {
return (
<div>
<h1>User Information</h1>
<User3 name="John Doe" age={30} />
</div>
);
};
export default Show;
RailsのViewでReactコンポーネントを呼び出す
app/views/users/show.html.erb
ビューファイルに javascript_include_tag
を使用して、Show.tsx
コンポーネントを含むJavaScriptファイルを含める。
<%= javascript_include_tag "controllers/users/Show", defer: true %>
defer: true
オプションを使用することで、スクリプトがHTMLの解析後に実行されるようになり、ページの読み込み速度が向上する。
動作確認
この設定により、Railsのルーティングとビューが正しく設定されている場合、ブラウザで http://localhost:3000/users/show
(または適切なポート番号)にアクセスすると、Show.tsx
に定義されたReactコンポーネントによってレンダリングされたページが表示される。
このページは、”User Information” という見出しとともに、”John Doe” の名前と 30 歳という年齢が表示される。
【Rails7】動的なカスタムエラー画面を作成し、共通デザインを適用する 方法に関しては、こちら。
コメント