💎

【Rails + React】RailsのView層でReactを書いてみる

2023/07/14に公開

はじめまして。
自分は元メーカーの営業職でWebエンジニアへの転職を志望し、主にRubyを中心に学習をしています。

色々触っている中でreact-railsをRailsプロジェクトに導入する経緯があったので、この記事ではその手順についてまとめています。

私はこれまでRubyonRailsを使ったアプリケーション開発を学んできました。近頃はReactなどフロントエンドも学習しているので、それらを合わせて何か作りたいなと考えていると、RailsアプリケーションのView層でReactのコンポーネントが使えるreact-railsというgemの存在を知りました。
https://github.com/reactjs/react-rails

基本的にはレールに乗っかってコードを書きながら、AJAXやバーコードスキャンの機能の部分だけReactで書きたいと考えていたので、react-railsはぴったりだと思いました。

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

Heroku有料化に伴い現在は停止中です。

基本的にはGitHubのドキュメントに書いてある手順に従って進めれば、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が見つからないよ」と怒られています。

以前Railsアプリケーションの開発を行なった際は、Reactは使わずに、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 gemの導入手順でした。
ここまでお付き合いいただき、ありがとうございました。

Railsだけでなくフレームワーク全体に言えることかもしれませんが、あれこれ考えずに開発を進められる一方で、その裏側で何が起きているのかについてはもっと理解を深める必要がありますね。
こうしてアウトプットをしてみると、色々気づかせてもらえる良い機会になりました。
今後はRailsはAPIモードのみ、フロントエンドはNext.jsで、といったような構成で何か作ってみようと思います。

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


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

Discussion