Reactを使ったRails環境でのフロント実装　〜Material UIの表示まで〜

#はじめに
Reactを書いていると、Material UIを使うことも多いかと思います。
それをRails環境でも使用できたらと思い、実際にやってみました。
ReactをRailsで使う方法はいくつかありますが、今回はwebpackerを使用して動かしていきます。
まずwebpackerとは何なのか、全体像を掴んでから始める方がいいと思いますので、以下の記事をざっと読んでからスタートすると後々の理解が深まると思います。
[webpack学習の基本のき](https://www.fundely.co.jp/blog/tech/2020/01/22/180037/)
もし間違えている箇所があればご指摘をいただければ幸いです。

#環境
- Ruby 2.5.1
- Rails 6.0.2.2
- Yarn 1.22.4
- Node v12.16.1

#まずは環境構築（Rails / React / webpacker）
使用するディレクトリを作成し、そのディレクトリに移動します。

```
$ mkdir myapp
$ cd myapp
```
Gemfileの作成

```
$ bundle init
Writing new Gemfile to /myapp/Gemfile
```
Gemfileの中のrailsのコメントアウトを外します。

```ruby:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails"
```

コメントアウトができたら、bundle installでgemをインストールします。

```
$ bundle install --path vendor/bundle
```
このように`bundle install`だけでなく、`--path`とパス指定を加えることで、そのアプリのディレクトリの中だけにインストールされます。
今回だとRailsアプリ（myapp）のルートディレクトリの下のvender/bundleディレクトリにインストールされます。
以下のような記載が出ればインストール完了です。

```
Bundle complete! 1 Gemfile dependency, 43 gems now installed.
Bundled gems are installed into `./vendor/bundle`
```
次に、以下のコマンドでwebpackerと同時にReactをインストールします。今回データベースはMySQLを使用したので末尾に`--database=mysql`と記載します。

```
$ bundle exec rails new . --webpack=react --database=mysql
```

Gemfileを上書きする確認のメッセージが出るので、yを入力してEnterで先に進みます。

```
       exist  
   identical  README.md
   identical  Rakefile
   identical  .ruby-version
   identical  config.ru
   identical  .gitignore
    conflict  Gemfile
Overwrite /myapp/Gemfile? (enter "h" for help) [Ynaqdhm]
```
各種インストールが始まります。

```
Initialized empty Git repository in /Users/katakawaryouhei/Desktop/React/myapp/.git/
      create  package.json
      create  app
      create  app/assets/config/manifest.js

---略---

Bundle complete! 17 Gemfile dependencies, 74 gems now installed.
Bundled gems are installed into `./vendor/bundle`

---略---

✨  Done in 6.89s.
Webpacker successfully installed 🎉 🍰
       rails  webpacker:install:react

---略---

✨  Done in 6.34s.
Webpacker now supports react.js 🎉

```

Railsで必要なファイル、webpacker、Reactのインストールが完了しました。
Gemfileを確認すると、しっかりwebpackerもインストールできていることがわかります。

```ruby:Gemfile
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# Use mysql as the database for Active Record
gem 'mysql2', '>= 0.4.4'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
```
またこの時点でjsxファイルが生成されています。

```jsx:app/javascript/packs/hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

---略---

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(document.createElement('div')),
  )
})
```

#Railsの動作確認

次にパスワードを打ち込み、MySQLを立ち上げます。

```yml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['RAILS_DATABASE_USER'] %>
  password: <%= ENV['RAILS_DATABASE_PASSWORD'] %>
  host: localhost
```
今回パスワード等の機密情報の管理は、`Dotenv-rails`というgemを使用して環境変数にしています。詳しくは以下の記事が参考になると思います。
[[Ruby on Rails]環境変数の設定方法(.bash_profile、Dotenv-rails)](https://qiita.com/yuichir43705457/items/7cfcae6546876086b849)

```
$ rails db:create
```

これでデータベースが立ち上がったので、Railsが動くか確認してみます。


```
$ bundle exec rails s
```

上記コマンド入力後、ブラウザのアドレス欄に localhost:3000 を入力します。
以下の画面が表示されればRailsが動作できています！
![スクリーンショット 2020-05-07 18.15.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493982/8d9f8d9c-3ce6-1db4-2f11-b5e9ce0a15c8.png)

動作確認ができたのでRailsサーバーは、Ctrl + C で一旦停止します。

#React動作確認
まずはReactを表示するための画面を作りたいので、コントローラーとビューを作成します。

```
$ bundle exec rails g controller home index

      create  app/controllers/home_controller.rb
       route  get 'home/index'
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      invoke  test_unit
      create    test/controllers/home_controller_test.rb
      invoke  helper
      create    app/helpers/home_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/home.scss
```
ルーティングは以下のように変更します。

```ruby:config/routes.rb
Rails.application.routes.draw do
  root to 'home#index'
end
```

続いて、Reactを表示させる為に、以下のように記載しERBファイルでJSファイルを読み込みます。

```erb:app/views/home/index.html.erb
<%= javascript_pack_tag 'application' %>
```
**`javascript_pack_tag`**は、JavaScriptをapp/javascript/packs配下から読み込むwebpacker用のヘルパーメソッドです。
**`'application'`**はapp/javascript/packs/application.jsを示します。
他のファイルを指定したい場合は、**`javascript_pack_tag '他のファイル名'`**と変更すれば、app/javascript/packs配下の別のファイルを読み込めます。
今回は**`'application'`**としました。application.jsを以下の1行を追加します。

```js:app/javascript/packs/appication.js
require('./hello_react.jsx')
```

それでは準備ができたのでサーバーを起動していきます。
開発環境では、Rails用のサーバーとwebpack用のサーバーの両方の起動が必要になるので、コマンドも別のターミナルでそれぞれ打ち込む必要があります。

```
$ bundle exec rails s
```

```
$ bin/webpack-dev-server
```

これでも『Hello React!』の画面が立ち上がるかと思いますが、package.jsonを以下の様に書き加えると、

```ruby:package.json
+ "scripts": {
    "start": "bundle exec rails s & bin/webpack-dev-server"
  },
```
以下のコマンド1回で済みます。

```
$ yarn start
```
上記コマンド入力後、ブラウザのアドレス欄に 再度localhost:3000 を入力します。
以下の画面が表示されればReactが動作できています！

![スクリーンショット 2020-05-07 22.39.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493982/f58064b7-3ff0-8273-3ed9-0e052159b3fe.png)


#動作確認後のスタイル変更（Material UIの導入）

Reactの動作が確認できたので、
- CSSの反映
- Reactコンポーネント分割
- Rails上でMaterial UIの導入
までやっていきたいと思います。

まずはCSSを反映させたいので、以下のように変更を加えます。

```erb:app/views/home/index.html.erb
<%= stylesheet_pack_tag 'application' %>
<%= javascript_pack_tag 'application' %>
```
先程の**`javascript_pack_tag`**に加えて、**`stylesheet_pack_tag 'application'`**を記載しました。
同じ要領で、application.jsに以下の1行を追加します。

```js:app/javascript/packs/appication.js
require('./stylesheets/hello_react.css')
```

まだファイルを作っていないので、新しくフォルダとファイルを作成します。
後々使うファイルも全て作成してしまいます。

```
app/
  └ javascript/
       └ packs/
           ├ stylesheets/
           │     └ hello_react.css
           ├ App.jsx
           ├ application.js
           └ hello_react.jsx
```

具体的には、以下を新規で作成しました。
- `stylesheets/`
- `stylesheets/hello_react.css`
- `App.jsx`

準備ができましたので、CSSファイルに記載を加えます。

```css:app/javascript/packs/stylesheets/hello_react.css
body {
  background-color: aqua;
}
```

わかりやすいように背景色を青色にしました。
これでリロードすると、CSSが反映されていることがわかります。
![スクリーンショット 2020-05-08 02.52.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493982/5ed1342c-12e3-643c-8660-e2177e088268.png)

それでは、React側でコンポーネントを切り分けていきます。
下記コードのように各ファイルの記載を変更します。

```jsx:app/javascript/packs/hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import App from './App'

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <App />,
    document.body.appendChild(document.createElement('div')),
  )
})
```

```jsx:app/javascript/packs/App.jsx
import React from 'react'

function App() {
  return (
    <div>
      <h2>これはReactです。</h2>
    </div>
  )
}

export default App
```

わかりやすいように、Ruby側のファイルもERB記法で下記のように追記します。

```erb:app/views/home/index.html.erb
<h2>これはRubyです。</h2>
<hr color="green">

<%= stylesheet_pack_tag 'application' %>
<%= javascript_pack_tag 'application' %>
```
これでリロードすると、ReactとRubyの両方で記載が完了していることがわかります。
![スクリーンショット 2020-05-08 03.05.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493982/1912097f-9d74-633b-2251-04d176b86e4a.png)
最後に、Material UIを表示させていきます。

```
$ yarn add @material-ui/core

warning @material-ui/core > popper.js@1.16.1: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...

---略---

├─ jss-plugin-nested@10.1.1
├─ jss-plugin-props-sort@10.1.1
├─ jss-plugin-rule-value-function@10.1.1
├─ jss-plugin-vendor-prefixer@10.1.1
├─ popper.js@1.16.1
└─ react-transition-group@4.4.1
✨  Done in 16.10s.
```

今回はCircle Progressを表示させてみるので、App.jsxを再度以下のように編集します。

```jsx:app/javascript/packs/App.jsx
import React from 'react'
import CircularProgress from '@material-ui/core/CircularProgress';

function App() {
  return (
    <div>
      <h2>これはReactです。</h2>
      <CircularProgress />
    </div>
  )
}

export default App
```

これでリロードすると、無事Material UIも表示できたかと思います！
![sampleapp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493982/2409a9fb-64d9-c995-44e1-917971b1455f.gif)

##参考
https://github.com/rails/webpacker
https://www.playfulit.net/articles/2019/01/27/webpacker-react-frontend-on-ruby-on-rails/
https://free-engineer.hatenablog.com/entry/2018/04/26/165941