【Rails, React】WebpackerとTypeScriptのセットアップ

# はじめに

作成中のRailsアプリのフロントエンドにReactを使いたかった。

Railsアプリ上でReactを利用する方法は複数あるらしい。


> RailsでReact.jsを使ういくつかの方法
2016年時点で、RailsでReact.jsを使う方法はいくつかあって、どれを採用するかで悩みました。

> 1. vendor/assets/javascripts にreact.jsを置いて利用する
2. react-rails gem を利用する
3. browserify-rails で npm管理して利用する
4. railsプロジェクト内に、JavaScript開発用のディレクトリを用意して webpack + babel-loader で利用する

> 調査したところ、だいたいこんなパターンがあると思っていて、下に行くほどRailsよりもJavaScript開発の知識が必要になってくるイメージでした。

[webpackを使った Rails上でのReact開発 - クックパッド開発者ブログ](https://techlife.cookpad.com/entry/2016/07/27/101015) より

できるだけモダンな方法を採用したかったが、

1. webpackをいきなり採用する自信がなかった
2. 仕事先でWebpackerを使っているプロジェクトがあった

という理由で、Railsのgemとしてwebpackを扱えるWebpackerを採用することに。

せっかくなので個人的に勉強していたTypeScriptも合わせて導入してみる。

# 方法

READMEをちゃんと読んで進める。

[rails/webpacker/README.md - Github](https://github.com/rails/webpacker/blob/master/README.md)


1.[Webpacker](https://github.com/rails/webpacker/blob/master/README.md#installation)のインストール

```Gemfile.
gem 'webpacker'
```
```
$bundle install
$bundle exec rails webpacker:install
```

自分がこれから設定していくWebpack環境の[ビルド](https://qiita.com/rico/items/9ab8aa110e757a13ef37)は以下のように行う。

[webpack-dev-server](https://github.com/rails/webpacker/blob/master/README.md#development)のビルド

```
$bin/webpack-dev-server
```

[webpack](https://github.com/rails/webpacker/blob/master/README.md#development)のビルド

```
$bin/webpack
```

2.[React](https://github.com/rails/webpacker/blob/master/README.md#react)を使えるようにする

```
$bundle exec rails webpacker:install:react
```

3.[TypeScript](https://github.com/rails/webpacker/blob/master/docs/typescript.md)を使えるようにする

```
$bundle exec rails webpacker:install:typescript
$yarn add @types/react @types/react-dom
```

4.jsxファイルをtsxに変更

```
$mv hello_react.jsx hello_react.tsx
```

5.tsxファイルの中身を編集

```app/javascript/packs/hello_react.tsx
import React from "react";
import ReactDOM from "react-dom";

interface HelloProps {
  name: string;
}

const Hello: React.SFC<HelloProps> = props => (
  <div>Hello {props.name}!</div>
)

Hello.defaultProps = {
  name: "David",
}

document.addEventListener("DOMContentLoaded", () => {
  ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(document.createElement("div")),
  )
})
```


[Webpacker で React + TypeScript - Qiita](https://qiita.com/kjugk/items/d9d39508f1899e6d147c) より

6.ビルドが成功したらhello_react.tsxファイルを挿入したい任意のRailsのviewsファイルに `<%= javascript_pack_tag 'hello_react' %>` という形で挿入。


```app/views/static_pages/index.html.erb
...
<section>
  <%= javascript_pack_tag 'hello_react' %>

</section>
...
```

7.1~5で作成されたor編集したファイルなど

```package.json
{
  "name": "MyRailsApp",
  "private": true,
  "dependencies": {
    "@babel/preset-react": "^7.0.0",
    "@rails/webpacker": "^4.0.6",
    "@types/react": "^16.8.23",
    "@types/react-dom": "^16.8.4",
    "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "ts-loader": "^6.0.4",
    "tslint": "^5.18.0",
    "tslint-config-airbnb": "^5.11.1",
    "tslint-loader": "^3.5.4",
    "typescript": "^3.5.2",
    "webpack-dev-server": "^3.5.1"
  }
}
```

```tsconfig.json
{
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "module": "es6",
    "moduleResolution": "node",
    "baseUrl": ".",
    "paths": {
        "*": ["node_modules/*", "app/javascript/*"]
    },
    "sourceMap": true,
    "target": "es5",
    "allowSyntheticDefaultImports": true,  // 追加
    "jsx": "react"  // 追加
  },
  "exclude": [
    "**/*.spec.ts",
    "node_modules",
    "vendor",
    "public"
  ],
  "compileOnSave": false
}
```

```tslint.json
// 追加↓
{
  "extends": ["tslint-config-airbnb", "tslint-react", "tslint-config-prettier"],
  "rules": {
    "variable-name": [true, "ban-keywords", "check-format", "allow-pascal-case"]
  }
}
```

```webpacker.yml
...
  extensions:
    - .jsx  // 追加
    - .tsx  
...
```

config/webpack/*.jsは特に修正しなかった気がする。
loaderの変更もややこそうだったのでなし。


```config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const typescript =  require('./loaders/typescript')

environment.loaders.prepend('typescript', typescript)
module.exports = environment
```

```config/webpack/loaders/typescript.js
const PnpWebpackPlugin = require('pnp-webpack-plugin')

module.exports = {
  test: /\.(ts|tsx)?(\.erb)?$/,
  use: [
    {
      loader: 'ts-loader',
      options: PnpWebpackPlugin.tsLoaderOptions()
    }
  ]
}
```


# 感想

いろんなファイルが生成されたのでどこをいじればいいかわからず大変だった。

Linterのエラーが出まくったりビルドが通らなくていろいろ修正したりして沼になったりしたが、READMEとエラー文に忠実に進めていくのが大事。


[Webpack の考え方について](https://mizchi.hatenablog.com/entry/2018/11/26/164523)
[]