Rails + TypeScript + React 開発環境構築

## 背景
- 夏休みの時間を使ってインターン活動で得た経験を復習したいと考え，webアプリケーションを作成することにしました．
- 実際のアプリケーションの他に，記事を書くことで，備忘録的な役割を持たせつつ，自分が学習した知識をアウトプットできると考えました．

## 環境
- Ruby 3.2.0
- Ruby on Rails 7.0.6

## 事前学習
環境構築の方法とは直説的に関係がありませんが，今回の記事の目的に，学習内容のアウトプットも含まれているので，本記事内に書き残しました．(飛ばしてokです)

諸先輩方による大変わかりやすい記事を参考にしながら，環境構築の方法を調査しつつ，今まで目を逸らしてきたRailsプロジェクトで実装したフロントエンドが表示されるそもそもの仕組みを勉強しました．

https://lef237.hatenablog.com/entry/2023/02/21/113432 

    
https://zenn.dev/naoki0722/articles/272ef57c6dafba

<details><summary>学習内容のメモ </summary>  

- Railsプロジェクト環境下でフロントエンドを実装し，ブラウザ上に表示するには，以下の処理が**必要であった．**
  - **トランスパイル** : JS以外のファイルをJSに変換する
  - **バンドリング** : 後者はモジュールの依存関係を解決し，JSが動くようにする
   
 - Rsils6まではwebpacker(Rails用のwebpackを楽に使えるようにするツール)というgemが使われており，webpackがモジュールバンドラとしての役割を担ってくれていた．
- Rails7では新たに，**importmaps**という仕組みが採用され，ブラウザがよしなに必要なファイル取得を行なってくれるので，バンドリングの処理が必要なくなった．(詳細は下記の参考文献をご覧ください．)
    - [全Rubyistに今すぐ伝えたいwebpackとwebpacker - Qiita](https://qiita.com/jesus_isao/items/1f519b2c6d53f336cadd)
   - [Rails 7.0 で標準になった importmap-rails とは何なのか？](https://zenn.dev/takeyuwebinc/articles/996adfac0d58fb)
    
- しかし，importmapsを利用する方法では，せいぜい「JSXのような形でJavaScriptを書けるようになる」だけで，JSX/TSXファイルは使うことができない．　→ **今回の環境構築の要件を満たすことができない**
    
- そこで，それらのファイルをトランスパイルして，要件を満たす(快適な)開発環境を構築するために，importmapsではなく，**jsbundling-rails**というビルドツールを使い，モジュールバンドらとして，**esbuild**を採用することで，TSXファイルを動かすことができるようになる．

</details>

**要点**
- Rails7から採用されたimportmapsではTSXファイルをトランスパイルすることができないので，別の方法を選択する必要がある．
- 代わりに**jsbundling-rails**というビルドツールを使って**esbuild**をモジュールバンドラに設定する
- ↑により，本記事の要件を満たす，環境(=React,TypeScript導入後にバンドリング，トランスパイルを行い，TSXファイルに記載したスクリプトが動作する)を構築する．


## 環境構築の手順
参考にした記事

https://zenn.dev/naoki0722/articles/272ef57c6dafba
1. [rails new](https://railsdoc.com/page/rails_new)!(新しいアプリケーションの作成)
下記のコマンドを実行すると，DBにpostgresqlに設定し，viewに`.tsx`ファイルを使用できるようになる．
    ```
    $ rails new (your app name) -d postgresql --javascript=esbuild
    ```

2. Reactの導入
    ```
    $ yarn add react react-dom
    ```

3. Gemfileの追加
Gemfileに`jsbundling-rails`([github](https://github.com/rails/jsbundling-rails))という名前のgemを新たに追加するために下記の文言を追記
    ```txt:Gemfile
    gem 'jsbundling-rails'
    ```
    その後，gemをインストールする
    ```
    $ bandle install
   ```
4. ビルドツールのインストール
    （[github](https://github.com/rails/jsbundling-rails)ページの指示に従い）以下のコマンドを実行
    ```
    $ rails javascript:install:esbuild
    ```

    <details><summary>実行結果</summary>  
        
    ```
    Compile into app/assets/builds
          create  app/assets/builds
          create  app/assets/builds/.keep
          append  app/assets/config/manifest.js
          append  .gitignore
          append  .gitignore
    Add JavaScript include tag in application layout
          insert  app/views/layouts/application.html.erb
    Add default Procfile.dev
          create  Procfile.dev
    Ensure foreman is installed
             run  gem install foreman from "."
    Fetching foreman-0.87.2.gem
    Successfully installed foreman-0.87.2
    1 gem installed
    
    A new release of RubyGems is available: 3.4.1 → 3.4.18!
    Run `gem update --system 3.4.18` to update your installation.
    
    Add bin/dev to start foreman
          create  bin/dev
    Install esbuild
             run  yarn add esbuild from "."
    yarn add v1.22.19
    [1/4] Resolving packages...
    [2/4] Fetching packages...
    [3/4] Linking dependencies...
    warning " > tempusdominus-bootstrap-4@5.39.2" has unmet peer dependency "moment-timezone@^0.5.31".
    warning " > tempusdominus-bootstrap-4@5.39.2" has unmet peer dependency "tempusdominus-core@5.19.3".
    [4/4] Building fresh packages...
    success Saved lockfile.
    success Saved 2 new dependencies.
    info Direct dependencies
    └─ esbuild@0.19.1
    info All dependencies
    ├─ @esbuild/linux-x64@0.19.1
    └─ esbuild@0.19.1
    $ husky install
    husky - Git hooks installed
    Done in 18.62s.
    Add build script
    Add "scripts": { "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets" } to your package.json
    ```        
    
    </details>


5. `package.json `に追記
    4の実行結果の指示に従う
    ```json:package.json
    // 追記
    "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets --target=esnext --loader:.png=file --loader:.js=jsx --loader:.ts=tsx",
    ...
    },
      "dependencies": {
         ...
        "esbuild": "^0.19.1",
    ```

6. 4の実行によって生成されたファイルの編集
    (foreman 経由でのサーバーを起動する際の設定
    ```text:Procfile.dev
    web: bin/rails server -p 3000 -b 0.0.0.0
    js: yarn build --watch
    ```

## 動作確認テスト
以下のファイルを作成して実際に表示されるかどうかを確認する．
(routeの設定方法等は割愛します．[参考](https://zenn.dev/naoki0722/articles/272ef57c6dafba#esbuild%E3%81%AB%E3%82%88%E3%82%8Bjavascript%E7%B7%A8%E9%9B%86%E6%96%B9%E6%B3%95(react%E5%B0%8E%E5%85%A5)))

viewファイル
```erb:app/views/home/index.html.erb
<div class="ly_container">

  <div class="card mt-30">
      <div class="de-top">
          <p><strong>Home#index</strong></p>
      </div>
  </div>
  <div id="homeIndex"></div>
  <%= javascript_include_tag "HomeIndex", defer: true %>
</div>
<script language="javascript" type="text/javascript">
</script>
```


```tsx:app/javascript/HomeIndex.tsx
import { HelloReact } from 'app/javascript/components/HelloReact/HelloReact';
import React from 'react';
import ReactDOM from 'react-dom';

export const PostIndex = () => {

  return (
    <>
      <HelloReact />
    </>
  );
};

ReactDOM.render(<PostIndex />, document.getElementById('postIndex'));

```

component
```tsx:app/javascript/components/HelloReact.tsx
import React from 'react';

export const HelloReact = () => {
  return (
    <h1>Hello React!</h1>
  )
}
```

設定ファイル
```javascript:app/javascript/controllers/index.js
// Import and register all your controllers from the importmap under controllers/*

import { application } from "./application"

// Eager load all controllers defined in the import map under controllers/**/*_controller
// import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)

// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
// lazyLoadControllersFrom("controllers", application)

```

`bin/dev`コマンドでサーバーを立ち上げる，編集したファイルのviewくぉ表示すると以下のように表示された．
![スクリーンショット 2023-08-12 23.09.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1071957/21e543a9-d8f1-d336-8b05-efdbd8575f1d.png)

## まとめ
動作確認のところでパスの指定等少々苦戦したものの，無事，RailsプロジェクトにReact,TypeScriptを導入することができました．
参考にさせていただいた記事のおかげで，技術背景なんかにも触れながら，比較的簡単に環境を構築することができました．


筆者自身学習をしながらの投稿になるので，所々文章が稚拙であったり，技術的な部分で誤りがあったりするかと思いますが，そのような箇所がございましたら，ご指摘いただけますと幸いです．
ここまで読んでくださりありがといございました．