車輪の二度漬け

railsを触っていましたが、最近はreactに興味あります。

RailsのviewにReactを使ったのでメモ(React + TypeScript + webpacker)

最近仕事でRailsで新規に作るviewにReactを使うということをやったので、それについて書いておく。

前提

  • これまではSprockets + CoffeeScriptで画面を作成していた
  • React + TypeScriptで新規画面を作りたい
  • 実際に画面を作るのはまだまだ先の話なので、現段階ではまずfeasibilityのチェックを優先してほしい
  • 実際に僕が実装をするのではなくReactやTypeScriptに明るくない別の同僚が実装をする可能性もある

やったこと

  • webpackerの導入
  • RailsAPI用コントローラーの作成
  • React + TypeScriptで書く

webpackerの導入

Reactが動く環境をどうやって導入するかをまず悩んだ。
個人的にはRailsがフロントエンドの管理まですることがあまり好きではないため、webpackを使ってReactの動作環境をつくろうと思っていた。 だが前提に書いたように、実際には僕以外の人間が実装を行う可能性があるため、webpackの設定を一から作っていかなければならないwebpackよりも、webpackをラップしたgemであるwebpackerを使うことにした。

webpackerインストール後の手順としては以下の通り。
フロントで使うパッケージは個別にインストールしてもいいと思うが、土台作りくらいはwebpackerのみでできるっぽい。

# webpackerの初回インストール
$ bundle exec rails webpacker:install

# React向けの設定
$ bundle exec rails webpacker:install:react

# TypeScript向けの設定
$ bundle exec rails webpacker:install:typescript

# React用の型定義追加
$ yarn add @types/react @types/react-dom

ちなみに、この記事を書いてる中で気づいたが、react-railsreact_on_railsっていうgemもあるらしい。これでもよかったかも。

RailsAPI用コントローラーの作成

これはよくあるrender :jsonでOK

React + TypeScriptで書く

ここからが本題。
ここに書いたのがベストプラクティスかは分からないが、参考になれば嬉しいので書いておく。

ディレクトリ構成

- /app
  - /javascripts
    - /packs
      - /entries
        - /someController
          - SomeControllerIndexEntry.tsx
          - SomeControllerShowEntry.tsx
    - /react
      - /someController
        - /pages
          - SomeControllerIndex.tsx
          - SomeControllerShow.tsx
      - /comp

まず、app/javascripts/packs/entries/someController直下には、各ページで使うためのReactファイルを配置する。
これはエントリーファイルなので、中身的には/app/javascripts/react/somecontroller/pagesに配置したファイルを呼び出すだけ。
/app/javascripts/react/somecontroller/pages直下には、そのページを構成するためのコンポーネントを書いたファイルを置く。
ページを構成する部品となる各コンポーネント/app/javascripts/react/somecontroller/componentsに置く。

あとは、この構成でビルドなどをするためにconfig/webpacker.ymlsource_entry_pathpacks/entriesにする。
こうすれば、

<%= javascript_pack_tag `someController/SomeControllerIndexEntry`%>

みたいにして、app/javascripts/packs/entries以下のディレクトリ構成通りに呼び出せるようになる。

この構成だと、./bin/webpackをしてビルドをすればpublic/packs/js/someControllers配下にバンドルされた各ページごとのファイルが出力されるはず。
というか、開発環境でもwebpack-dev-serverを起動してない && public〜配下にバンドル済のファイルがないという場合にはwebpacker側がバンドルをするっぽいね。

csrf_tokenについて

今回はPOST処理も必要だったので、JSでよく使われてるFormDataを使ってPOSTをしようと思ったところcsrf tokenがないからPOSTを受け付けられないという旨のエラーが出た。
Railsのviewのmetaタグにはデフォルトで

<meta name="csrf-param" content="authenticity_token" /> 
<meta name="csrf-token" content="csrfToken" />

というものがあるので、

const csrfParam = (document.querySelector('meta[name="csrf-param"]') as HTMLMetaElement).content
const csrfToken = (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement).content

こういう風に取って、

const form = new FormData()
form.append(csrfParam, csrfToken)

というようにしてformに混ぜ込めば大丈夫。