Rails 7 + React 19 を Vite で動かす環境構築（Docker対応）

## 概要

- Vite を使って Rails アプリに React を導入する方法。
- Docker 環境で起こった WebSocket/HMR のエラーとその対処法

## 前提条件
- Rails7.2
- React19.0
- vite 最新

## ディレクトリ構成
```
/rails_app
├── Dockerfile
├── docker-compose.yml
├── app
```

## 手順
- Vite Rails の導入
- React のインストール
- Vite 設定ファイルの編集
- application.html.erbの修正


## 実装
https://vite-ruby.netlify.app/guide/
の手順通りに進めます
```rb
gem 'vite_rails'
```
を記述した後、以下を順番に実行します

```bash
bundle install
bundle exec vite install #Vite環境をRailsに組み込む
yarn add react react-dom #React本体
yarn add -D @vitejs/plugin-react #ViteでReactを使うためのplugin
```

yarn addでreactとreact-domを入れる
```bash
yarn add react@latest react-dom@latest
yarn add -D @vitejs/plugin-react
```

`vite.config.ts`にreact()を追記します
```ts
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import react from "@vitejs/plugin-react"

export default defineConfig({
  plugins: [
    RubyPlugin(),
    react()
  ],
})

```
`application.html.erb`を修正します
```erb

<!DOCTYPE html>
<html>
  <head>
    <title>ReactOnRails</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= vite_client_tag %>
    <%= vite_react_refresh_tag %>
    <%= vite_javascript_tag 'application.jsx' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

```
- `vite_client_tag` → Vite の HMR クライアント読み込み（開発中のみ）
- `vite_react_refresh_tag` → React コンポーネントの状態を保ったままリロードするためのタグ（React の Fast Refresh 対応）
- `vite_javascript_tag 'application'` → エントリポイントを読み込み（`app/javascript/entrypoints/application.jsx` を指す）


## 参考記事

https://github.com/ElMassimo/vite_ruby?tab=readme-ov-file

https://kei-kmj.hatenablog.com/entry/2022/10/07/103158

https://tech.fusic.co.jp/posts/2022-07-07-vite-rails-react/#vite_rails%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

https://vite-ruby.netlify.app/guide/



## 以下のエラーが起こっていた
```
WebSocket connection to 'ws://0.0.0.0:3036/vite-dev/?token=v6TJU8fD7wce' failed: 
```
- 0.0.0.0は「すべてのネットワークインターフェースでリッスンする」という意味のIPアドレス
- クライアント（ブラウザ）が接続する際のアドレスとしては機能しない
- localhostに変更することで、ブラウザは実際にアクセス可能なアドレスに接続できるようになる
矢じるし
- サーバー側（Vite）では「すべてのインターフェースでリッスン」するために`0.0.0.0`を使う
- クライアント側（ブラウザ）では実際に接続できるアドレス（`localhost`）を使う


`vite.json`
```json
  "development": {
    "autoBuild": true,
    "host": "0.0.0.0",
    "publicOutputDir": "vite-dev",
    "port": 3036
  }
```

`docker-compose.yml`
```yml
environment:
  TZ: Asia/Tokyo
  VITE_RUBY_HOST: "0.0.0.0"
  VITE_RUBY_PORT: 3036
```
`vite.config`

```config
import { defineConfig } from 'vite'
import RubyPlugin from 'vite-plugin-ruby'
import react from "@vitejs/plugin-react"

export default defineConfig({
  server: {
    host: '0.0.0.0',
    port: 3036,
    hmr: {
      protocol: 'ws',
      host: 'localhost',
      port: 3036,
    }
  },
  plugins: [
    RubyPlugin(),
    react()
  ],
})

```

## 参考記事

https://vite-ruby.netlify.app/guide/troubleshooting.html#making-hmr-work-in-docker-compose
> VITE_RUBY_HOSTの設定

https://ja.vite.dev/config/server-options#server-host
> WebSocket もプロキシするようにリバースプロキシを設定する
デフォルトの設定では、Vite の前のリバースプロキシが WebSocket のプロキシに対応していることが期待されています。Vite の HMR クライアントが WebSocket の接続に失敗した場合、クライアントはリバースプロキシを迂回して直接 Vite の HMR サーバーに接続するようにフォールバックします:
`Direct websocket connection fallback. Check out https://vite.dev/config/server-options.html#server-hmr to remove the previous connection error.`
フォールバックが発生した際のブラウザーに表示されるエラーは無視できます。直接リバースプロキシを迂回してエラーを回避するには、次のいずれかを行えます:
 WebSocket もプロキシするようにリバースプロキシを設定する
[**`server.strictPort = true`**](https://ja.vite.dev/config/server-options#server-strictport) を設定し、`server.hmr.clientPort` を `server.port` と同じ値に設定する
`server.hmr.port` を [**`server.port`**](https://ja.vite.dev/config/server-options#server-port) とは異なる値に設定する


https://sadanora.hatenablog.com/entry/2023/03/31/122425
> localhostのデフォルトが::1(IPv6) のシステムだとこのエラーが発生するらしく、hostを明示的に構成すればOKとある。
ということでconfig/vite.jsonのdevelopmentにhostを明示したらコンソールからエラーが消えた。


https://madogiwa0124.hatenablog.com/entry/2024/06/02/141429
> vite.config.tsでhostを指定しても変わらなかったのは、vite-plugin-railsでserver.hostをデフォルトでlocalhostにしており、上書きされてしまいvite.config.tsの設定が反映されなかったっぽい😭