Help us understand the problem. What is going on with this article?

RailsアプリにWebpacker(React + 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開発 - クックパッド開発者ブログ より

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

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

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

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

方法

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

rails/webpacker/README.md - Github

1.Webpackerのインストール

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

自分がこれから設定していくWebpack環境のビルドは以下のように行う。

webpack-dev-serverのビルド

$bin/webpack-dev-server

webpackのビルド

$bin/webpack

2.Reactを使えるようにする

$bundle exec rails webpacker:install:react

3.TypeScriptを使えるようにする

$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 より

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 の考え方について

yokoto
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした