webpackerを使ったRails6.1アプリへのReact18を導入
'react-rails'gemを使うことなく、webpackerの機能だけを用いて、Reactを使えるようしてみたので、簡単にまとめていきます。
検証環境
Ruby 3.0.4
Rails 6.1.7
React 18.2.0
(参考にした資料)
youtubeのものは、tutorial形式かつ、Railsプロジェクトでview部をreactのみで作成しているので、大変参考になりました。
webpackerをつかってReactを使えるようにする。
今から新規で作成するプロジェクトの場合、ReactとRailsのアプリを分離して作成した上で、API連携させることになることが多いのかと思いますので、今回は、既存のプロジェクトであるという前提で進めます。
reactの導入
$ rails webpacker:install:react
cf)
もし、新規アプリの場合
$ bin/rails new <app名> --webpack=react
これによって、react関連のパッケージの導入と、app/javascript/packs/hello_react.jsx
ファイルが生成されます。
<%= javascript_pack_tag ‘hello_react’ %>
layoutの<head>内にjavascript_pack_tag ‘hello_react’
を記述することで、railsアプリが、hello_react.jsxファイル内のコンポーネントをレンダーするようになります。
コントローラを作成して、Helloコンポーネントがレンダーされるか確認する
実際のviewに反映されているかを、テスト用のcontrollerを追加して確認します。
$ rails g controller site index
“/”へのアクセスで今作成したページが表示されるようにルーティングを変更します。
# config/routes
Rails.application.routes.draw do
get 'site/index'
root 'site#index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
以上で、コンポーネントを無事描画できるようになっているはずです。
(HelloコンポーネントはHello React!を出力するだけのコンポーネントです)
問題が残っていた。
特に問題はないと思いながらも、ブラウザのコンソールを開くと、
Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot
とエラーが吐かれてしまいました。どうやら、デフォルトのhello_react.jsxの記法がReact 17流の書き方だそうです。特に、ReactDOM.renderの部分。
(僕がreactに入門したのはすでに18になっていたので、違いについてはあまりよくわかっていません。)
React18流に、hello_react.jsxを変更する
生成されたhello_react.jsxは、以下のようになっています。
:before
// hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
const Hello = props => (
<div>Hello {props.name}!</div>
)
Hello.defaultProps = {
name: 'David'
}
Hello.propTypes = {
name: PropTypes.string
}
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<Hello name="React" />,
document.body.appendChild(document.createElement('div')),
)
})
エラーのいうことを聞きながら変更を加えると以下のようになりました。
:after
// hello_react.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
const Hello = props => (
<div>Hello {props.name}!</div>
)
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(
<Hello name="React" />,
);
ここままだと、#=”app”を持つHTML要素はどこにもないので、application.html.erbに空のdivタグを追加します。
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag "hello_react" %>
</head>
<body>
<div id="app"></div>
<%= yield %>
</body>
</html>
この<div>要素がReactをマウントするrootとして機能するようになります。
あとは、javascript_pack_tagを埋め込むだけです。
ページ全体をreactをつかって描画したい場合には、
e.g.)
<body>
<div id="app"></div>
<%= javascript_pack_tag 'hello_react' %>
<%= yield %>
</body>
アプリの一部だけをreactにしたい場合には、そのページにて
React要素をマウントするroot(<div id="app"></div>
)を設置し、それに続くようにjavascript_pack_tag
メソッドで、コンポーネントをレンダーするようにして下さい。
蛇足
その後、スタイリングがあまり得意ではないので、ChakraUIに頼ろうとしましたが、依存性があるパッケージを全て導入したような気がするものの、何故か使えませんでした...
React on Railsで導入する際には、色々と苦労することが多そうです。
Discussion