10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Railsアプリケーションを途中からReactのSPAに書き換える際に設定したこと

Posted at

この記事の内容

既存Railsアプリを徐々にSPAに置き換えていく、という課題を研修でやりました。
Reactの設定ファイルは先輩が書いてくださったので、その内容を読み解きながら、RailsアプリにReactを導入していく流れを確認しようと思います。

なお、実行環境は以下の通りです。

  • Rails 6.0.3
  • React 17.0.2

Rails6系なのでWebpackerはデフォルトで入っているものとします。

ディレクトリ構成

今回の(最終的な)ディレクトリ構成は下記の通りです。関連のあるところだけ記載しています。なお、ここに記載のファイル以外に、config/routes.rbも編集しています。

.
├── controllers
│   └── spa
│       └── spa_roots_controller.rb
└── javascript
    ├── App.jsx
    ├── Routes.jsx
    ├── components
    │   └── Bar.jsx
    ├── packs
    │   ├── application.js
    │   ├── root_application.jsx
    │   └── server_rendering.js
    └── pages
        └── Foo.jsx

ゴール画面

今回のゴール像は以下のイメージです。

Image from Gyazo

Fooページの中にBarコンポーネントを表示したいと思います。

Reactをインストール

まず、gem react-railsをインストールします。

Gemgile
gem 'react-rails'

このGemを利用する以外の方法も調べはしたのですが、今回はコードの読み解きに留めることにしました。

なお、このgemは先に紹介した記事によると、「RailsのAsset Pipelineを利用してJSXをRailsが認識できる形に処理し」てくれるそうです。

公式サイトの記載通りに、コマンドを実行します。

$ bundle install
$ rails webpacker:install
$ rails webpacker:install:react
$ rails generate react:install

その結果、以下のファイルが生成・追記されます。

app/javascript/components/ # Reactのコンポーネント用に生成
app/javascript/packs/application.js # ReactRailsUJSの記載が追記
app/javascript/packs/server_rendering.js # サーバーサイドのレンダリング用に生成

ここで、application.jsserver_rendering.jsには同じ内容が記載されていましたが...。

var componentRequireContext = require.context("components", true);
var ReactRailsUJS = require("react_ujs");
ReactRailsUJS.useContext(componentRequireContext);

require.context("components", true);は、/componentディレクトリ下の全てのファイルを拾うという設定、useContext(設定)は、設定に書かれた要素がグローバルな要素として使われるという記載のようです。

それぞれ、以下を参考にしました。

RailsのルーティングとControllerの編集

次にRails側の、設定ファイルを書いていきます。
まずはconfig/routes.rbに下記のように記載します。

config/routes.rb
scope 'spa' do
  get '*path', to: 'spa_roots#show'
end

こちらの記述によって、/spa/*で始まるリクエストが来たらなんでも、spa_rootsコンロトーラーのshowアクションに飛ぶようにします。

namespaceではなくてscopeである理由は、先輩が参考記事を貼ってくれていました。)

そして、spa_routes_controllerには、ほぼ何も書かず...。

app/controllers/spa_roots_controller.rb
class SpaRootsController < ApplicationController
  def show; end
end

代わりに、spa_roots#showのViewの方にエントリーポイントとなるファイルを読み込む記載を入れます。

app/views/spa/spa_roots/show.slim
= javascript_pack_tag 'root_application'

この時読み込んでいるroot_applicationには何が書かれているかというと、以下のとおりです。

app/javascript/packs/root_application.jsx
import { App } from '../App'
import React from 'react'
import ReactDOM from 'react-dom'

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

ReactDOM.renderは以下のような形で使われ、React公式サイトによると、

ReactDOM.render(element, container[, callback])

渡された container の DOM に React 要素をレンダーし、コンポーネントへの参照(ステートレスコンポーネントの場合は null)を返します。

とのことですので、

document.body.appendChild(document.createElement('div') の部分で、bodyの末尾に作られた<div>タグに<App />をレンダーさせます。

Routerの導入

では、先程記載したこの<APP />には何が書かれているのかというと、以下の内容です。

app/javascript/App.jsx
import React from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import { Routes } from './Routes'

export const App = () => {
  return (
    <Router>
      <Routes />
    </Router>
  )
}

Routerが出てきましたね。実は、順番が前後してしまいましたが、この少し前にreact-router-domをインストールしました。

yarn add react-router-dom

そして、app/javascript/App.jsxではBrowserRouter(Routerという名前で使用)とRoutesというライブラリを読み込んでいます。

それぞれのライブラリに関して深入りはしませんが、

  • BrowserRouter ... ルーティングの切り替え機能を提供
  • Routes ... それぞれのルーティングを提供

しているようです。

試しにページを表示

そして、この<Routes />には次のように書いてありました。<Switch />を使って、リクエストごとに表示する内容を切り替えていきます。

  • APIドキュメント:Switch
app/javascript/Routes.jsx
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import { Foo } from '~/pages/Foo'

export const Routes = () => {
  return (
    <Switch>
      <Route path="/spa/foo">
        <Foo />
      </Route>
    </Switch >
  )
}

ここまでで、Fooコンポーネントは表示されます。

Image from Gyazo

この、Fooコンポーネント内で、さらにBarコンポーネントを呼び出せば、完成です。

app/javascript/pages/Foo.jsx
import React from 'react'
import { Bar } from '~/components/Bar'

export const Foo = () => {
  return (
    <div>
      <h1>これはfooページです</h1>
      <Bar />
    </div>
  )
}

完成!

以上で、RailsにReactを途中から導入する設定が完了です。
いやー、、、先輩いなかったら完全に彷徨っていたと思います。。。

まだまだJSはわからないことが多いので、これからも地道に調べながら理解を深めていきたいです。

10
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?