方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

【Rails7 + React + TypeScript】によるフロントエンド開発

この記事は約13分で読めます。

はじめに

今回、新しいプロジェクトで、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-railscssbundling-railsが導入され、バンドル方法がよりシンプルかつ直感的になった。

jsbundling-railsは、JavaScriptのバンドルに関する新しいアプローチで、esbuildrollup.jswebpackなど、複数のバンドラーをサポートしている。

esbuildは極めて高速なJavaScriptバンドラーおよびミニファイヤーで、大規模なJavaScriptコードベースでも素早くバンドルすることができる。

Railsプロジェクトにjsbundling-railsesbuildを導入するには、以下の手順を実行。

jsbundling-railsをGemfileに追加してインストール:

ShellScript
# Gemfile
gem 'jsbundling-rails'

その後、bundle installを実行。

esbuildをセットアップ:

ShellScript
bin/rails javascript:install:esbuild

これにより、esbuildがセットアップされ、app/javascriptディレクトリ内で利用可能になる。

TypeScriptのセットアップ

RailsプロジェクトにTypeScriptを導入するためには、まず以下のコマンドを実行して必要なパッケージをインストールする。

ShellScript
yarn add typescript @types/react @types/react-dom @babel/preset-typescript

このコマンドにより、TypeScriptとそのReact用の型定義、そしてBabelのTypeScriptプリセットがプロジェクトに追加される。

RailsでのPathとアクションの設定

まず、Railsで画面を描画するためのPathを作成する

Dockerコマンドを利用したコントローラーとアクションの自動生成

下記コマンドで新しいコントローラーとアクションを自動生成する。

ShellScript
$ docker-compose run --rm app bin/rails generate controller [コントローラー名] [アクション名]

例えば、「Users」というコントローラーに「show」というアクションを追加したい場合、次のようにコマンドを実行する。

ShellScript
$ 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.rbshowメソッドが追加され、関連するViewファイルがapp/views/usersディレクトリに作成される。

このとき、自動的にtestファイルやhelperファイルも作成される。もし不要であれば、削除する。

Routingの設定

Railsにおいて、RoutingはURLとコントローラーのアクションを結びつける役割を持つ。config/routes.rbファイルを編集することで、ルーティングを設定できる。

以下は、UsersControllershowアクションに対応するルートを定義する例。ここで:idはパラメータとして、ユーザーのIDを動的に受け取ることを示している。

Ruby
Rails.application.routes.draw do
  get 'users/:id', to: 'users#show'
end

app/controllers/users_controller.rbshowアクションを定義する。このアクションは、対応するビューをレンダリングするために使用される。

Ruby
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を引数に取り、nameageプロパティを使ってUIを構築し、それを返している。

ここでのUser変数は、Reactのコンテキストでは「コンポーネント」として扱われる。これを他のReactコンポーネントから<User name="John Doe" age={30} />のように使用することで、そのユーザー情報を表示することができる。

文字列などのプリミティブな値を直接渡す場合は引用符("")を使用。

JavaScriptの式や変数の値を渡す場合は、波括弧{}を使用してその式を囲む。

TypeScript
// 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ファイルを作成し、以下のように記述する。

ファイルのパスと命名:

ShellScript
app/javascript/controllers/users/Show.tsx

この構造では、app/javascript/controllers/ ディレクトリの下にバックエンドのコントローラー名に対応するディレクトリを作成し(この場合は users/)、その中に対応するアクションメソッド名に基づいた .tsx ファイルを作成している。

ここでは、UsersControllershow アクションに対応して Show.tsx というファイルを作成。

UserコンポーネントをUserという名前でインポートし、固定のデータを渡してユーザー情報を表示。

TypeScript
// 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.tsxUser1 コンポーネントを定義し、デフォルトエクスポートし、別のファイルでこのコンポーネントを import User3 from '../components/User'; としてインポートしている。ただし、基本的には可読性の観点から、すべてUserで統一した方が良い。

TypeScript
// 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ファイルを含める。

Ruby
<%= 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】動的なカスタムエラー画面を作成し、共通デザインを適用する 方法に関しては、こちら。

コメント

タイトルとURLをコピーしました