💎

【Rails + React】RailsのVies層でReactを書く

2023/07/14に公開

はじめまして、あつむです。今回が初めての投稿になります。よろしくお願いいたします。
これまでRubyonRailsを使ったアプリケーション開発を学んできました。近頃はReactなどフロントエンドも学習しているので、それらを合わせて何か作りたいなと考えていると、RailsアプリケーションのView層でReactのコンポーネントが使えるreact-railsというgemの存在を知りました。

実際に開発したアプリケーションはこちらです。
https://library-passbook.herokuapp.com/
読んだ本を通帳に記帳していくように管理するアプリです。
(モバイル端末での使用を想定しています)

ここでは、知識定着のためにもreact-railsを用いて、Rails上でReactコンポーネントを使用するための準備手順をまとめさせていただきたいと思います。
RailsアプリケーションにAJAXやバーコードスキャンの機能を持たせる際、一部Reactで実装したものをRailsに乗せるという構成をとった上記のアプリケーションを開発したのは1ヶ月ほど前です。
その時に使用したのが、react-railsのgemです。
https://github.com/reactjs/react-rails

基本的には書いてくれている手順に従って進めれば、View層で呼び出したReactコンポーネントをブラウザでも確認できる状態になります。ただ、少しアップデートがあった(Webpackerの部分で変更があったらしい?)ようで、今回改めて試してみたところ、一部手順通りではうまくいかない部分もありました。自分なりにこうだろうと理解したものを、以下のとおり共有させていただきます。

利用バージョン

#ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin20]
#rails -v
Rails 7.0.6
#node -v
v16.15.1

1.Railsアプリケーションを準備する

rails newで新しいプロジェクトを作り、そのプロジェクトのディレクトリへ移動します。
Reactコンポーネントを使用するためにはモジュールバンドラとして、Webpacker/Shakapackerを使用する必要があり、それらは別途インストールを行います。なので、rails newを行う際には、Railsのデフォルトのjavascript関連のファイルは不要の旨を明言します。

rails new my-app --skip-javascript
cd my-app

2.Shakapackerをインストールする

bundle add shakapacker --strict
rails shakapacker:install

手順では、rails webpacker:installを実行するようにと記載があったのですが、
コマンドラインに非推奨を意味するメッセージが表示されました。
また、メッセージによると、代わりにshakapacker:installを実行した方がええで、とのことなのでこちらを使用します。

3.必要なパッケージをインストールし、package.jsonのBabelの構成を修正する

yarn add react react-dom @babel/preset-react prop-types \
css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin
package.json
"babel": {
  "presets": [
-   "./node_modules/shakapacker/package/babel/preset.js"
+   "./node_modules/shakapacker/package/babel/preset.js",
+   "@babel/preset-react"
  ]
},

4.react-rails gemをインストールする

react-railsのGemfileをインストールします。

bundle add 'react-rails' --strict
rails generate react:install

このコマンドによる差分は以下の通りです。
-app/javascriptディレクトリ配下にcomponentsというフォルダが作成される。
-app/javascript/packsというフォルダが用意され、その中に、application.jsserver_rendering.jsというファイルが生成される。

今後色々作っていくReactコンポーネントはこのcomponentsフォルダ内で管理します。
このフォルダ内には任意でフォルダを作ることができるので、atomicデザインなど、特定のルールに従って開発を進めることも可能です。むしろ、作業していると、コンポーネントがどんどん散らかってくる上に、どのコンポーネントをrailsのViewで使っているのか把握しきれなくなってくるので、要素として細かいコンポーネント(他のコンポーネントにインポートして使うコンポーネント)、大きいコンポーネント(あらかた形として整ったコンポーネント)、Railsが使用するコンポーネント、とルール決めをしてファイルを整理した方がいいです。

app/javascript/packsフォルダは、Webpacker/Shakapackerがコンパイルする際に、エントリポイントとなるファイルを格納します。このフォルダ内にあるapplication.jsの情報をもとにWebpackerがjs関連のファイルをなんかええ感じに実行してくれます。server_rendering.jsについてはまだよく理解できていません。(前回開発した際、このファイルは触りませんでした。)

4-1.Rails上でReactを使う上での注意点

Bootstrapも使いながら開発を進めたい方もおられると思います。
ただ、このBootstrapも使いながらReactコンポーネントを使いたいというわがままにより、盛大につまづくこととなったので、その原因を簡単に共有したいと思います。(解決法については、また別の記事としてまとめたいと思います。)

まず、Bootstrapを使えるようにするために、RailsデフォルトのバンドラであるSprocketsを利用したアセットパイプラインでBootstrapをインポート(app/assets/stylesheet/application.scss@import "bootstrap/scss/bootstrap.scss";を記述)していました。開発環境では問題なく、Bootstrapの内容も反映されていたのですが、これをHerokuを使って、デプロイしようとすると、ビルド時にエラーが出ました。
その内容が、

remote:        Running: rake assets:precompile
remote:        rake aborted!
remote:        SassC::SyntaxError: Error: File to import not found or unreadable: bootstrap/scss/bootstrap.
remote:                on line 7:1 of app/assets/stylesheets/application.scss
remote:        >> @import "bootstrap/scss/bootstrap";

というもので、「packディレクトリ内のapplication.jsでbootstrapが適切にインポートされていないがために、bootstrapが見つからないよ」と仰られています。

これがBootstrapも使いながらReactコンポーネントを使いたいという欲張り人間が、コケるべくしてコケるポイントだと思いました。
以前、Reactを使わず、Railsアプリケーションの開発を行なった際は、BootstrapとSassのみでViewのスタイルを調整したので、Webpackerを使う必要がありませんでした。そして、今回も前回の経験をもとに導入を進めておりました。
この時、Bootstrapを使用可能としてくれていたのは、先ほどさらりと書いてしまいましたが、Railsにデフォルトで用意されているモジュールバンドラであるSprocketsの働きによるものでした。ただし、SprocketsはReactをカバーしていないので、別途Webpackerをインストールする必要があります。そして、Webpackerは"4.react-rails gemをインストールする"でも言及しましたが、app/javascript/packsにあるapplication.jsの情報をもとにコンパイルを行います。
そして、このapplication.jsにはBootstrap関連の記述はしていませんでした。
これでも、開発環境では問題は起こりません。それは開発環境でアプリケーションを起動する際、Railsのサーバーと同時に稼働させる.bin/webpack-dev-serverでJavaScriptやCSSのソースコードの変更を自動検知して、ブラウザに自動で反映してくれているからです。

そして、Herokuがプロジェクトの本番環境を構築する際、Gemfileやpackage.jsonで諸々インポートされたライブラリはbuildpackという機能を用いて実行されます。Herokuのbuildpackは公式ドキュメントのリストにまとめられている言語をサポートしています。そして、このリストの順番にbuildpackが検索され、一致したものからコンパイルするという構造になっています。そうすると、Rubyが一番初めにあるのでRubyのbuildpackが実行され、次にNode.jsのbuildpackが実行されるという流れになります。
ここからはまだあまり理解できていませんが、Rubyのbuildpackが先に実行される際、Webpackerのコンパイルが始まるために、「Bootstrapを使うみたいだけど、Bootstrapは見つからないよ」というエラーになるものだと理解しました。
別記事でまとめますが、この時に、herokuのコマンドで、Node.jsのbuildpackをRubyのbuildpackより先に実行するように設定すれば解決しました。

5.Railsでルーティングとコントローラー、Viewファイルを準備する

本題に戻ります。
Reactコンポーネントを実行するためには何はともあれ、Railsアプリでブラウザに画面表示をしなければならないので、Rails側の設定を行います。
今回はとりあえずブラウザに画面が表示されれば良いので、簡易に以下のように設定しました。

route.rb
Rails.application.routes.draw do
  root "pages#home"
end

コマンドラインで以下を実行します。

rails g controller pages

生成されたコントローラーファイルに以下を記述。

pages_controller.rb
class PagesController < ApplicationController
  def home
  end
end

生成されているviews/pagesディレクトリ内に、home.html.erbを作成します。
念の為確認できるように以下のように記述しました。

home.html.erb
<h1>Hello from Rails</h1>

6.Reactコンポーネントをつくる

では、先ほど作成したhome.html.erb上にReactコンポーネントを反映させていきまそう。
react-railsではReactコンポーネントを作るためのコマンドが用意されています。

rails g react:component <コンポーネント名> <propsを渡す場合はprops名:とその値のデータ型>

ただし、このコマンドでやると、クラスコンポーネントが生成されてしまうので、個人的には直接componentディレクトリ以下のファイルにjsxファイルを用意して、直接関数コンポーネントを書いていく方が、何かと使い勝手は良さそうな気がしました。

では、componentファイル内に、testReact.jsxというファイルを作成します。

testReact.jsx
import React from 'react';

const testReact = () => {
  return (
     <h1>Hello from React</h1>
  );
};

export default testReact;

7.RailsのViewファイルでReactコンポーネントを展開する。

Reactコンポーネントを使用する際は、RailsのViewファイルで

<%= react_component("<コンポーネント名>") %>

のように記述します。
#6で用意したhome.html.erbファイルで先ほど用意したReactコンポーネントを使用する場合は以下追記します。

home.html.erb
<%= react_component("testReact") %>

Viewファイル内で使用している変数をReactコンポーネントに渡す場合はPropsを使うことができます。
Propsを渡す必要のあるReactコンポーネントを使用する場合は、以下のように書きます。

<%= react_component("<コンポーネント名>",{<props名>, <propsに渡す値>}) %>

8.ブラウザで確認してみる

ここまで実装ができたら、ブラウザで確認してみましょう。
#4-1の部分でも少し触れましたが、Webpackerを使用しているRailsアプリケーションを起動するには、

rails s

の他に、もう一つ別のコンソール画面を開き、

./bin/shakapacker-dev-server

を実行すればokです。

この部分ですが、手順に沿って進めていると、

./bin/webpacker-dev-server

と書かれています。確かに、私が前回開発していた際はこれでも問題ありませんでした。
ただ、今回試してみると、

./bin/webpacker-dev-server
zsh: no such file or directory: ./bin/webpacker-dev-server

というエラーが返され、binフォルダの中にWebpacker...のファイル/ディレクトリが見つかりませんと仰られました。
binフォルダを実際に見てみると、Webpacker関連のファイルは見当たりません。
代わりにshakapacker-dev-serverというファイルがあったので、上記の通りコマンドを実行してみると、問題なく動作しました。

ブラウザ画面を確認すると……

このように、Reactの部分もうまく反映されていますね。

以上長くなりましたが、react-railsことはじめでした。
ここまでお読みいただき、ありがとうございました。

途中にも書きましたが、Herokuを使っての本番環境へのデプロイの際に、つまづいた部分があったので、その原因のおさらいと解決法についてはまた別の記事で共有させていただきたいと思います。

Railsだけでなくフレームワーク全体に言えることかもしれませんが、開発しやすくしてくれている一方で、いろいろ動作している裏側で何が起きているのかがわかりにくいですね。
こうしてアウトプットをしてみると、色々気づかせてもらえる良い機会になりました。
今後はRailsはAPIモードのみ、フロントエンドはNext.jsで、といったような開発にも取り組んでいこうと思います。

今回の記事をまとめるにあたり、以下のリンク先記事及び資料を参考にさせていただきました。
改めて、御礼申し上げます。


react-rails
Rails×React×TypeScriptで作るWEBアプリ入門
Heroku-buildpack
Rails-アセットパイプライン

Discussion