jQueryしかしてこなかったけどそろそろReactをやる

これまでDOMの操作はjQueryくらいしか使ってなかったのですが、頑張ってReactに手を出してみました。
備忘録的な意味合いが強いですが、自分のような初学者がReact周りの用語の意味をなんとなく掴むのには役立つかもしれないので記事にしてみました。


# React？
- 既に存在するDOM要素を操作する事に長けるjQueryと違って、動的にDOM要素を生成するときにも便利
    - 別にjQueryと一緒に使っても良い
- jsxというテンプレート記法で書けばjsコード内にHTMLタグをほぼそのまま書ける
    - 当然そのままでは動かないのでjsコードに変換してやる必要はある
    - jsxの構文自体は簡単
- jsxを変換したりする必要があるのでモジュールバンドラとかESコンパイラで自動的に変換しようぜ！
    - この辺からだんだん怪しくなってくる
    - webpackとBabelを使った
- ReactはUIをコンポーネントにできる
    - UIのライブラリもたくさん
    - react-toolboxが使ってみたかったのもReact習得の動機の一つ

# 登場人物
現代のWEB開発は環境がややこしい。

## [React](https://reactjs.org/docs/getting-started.html)
主人公。Facebook生まれ。
動作させるには最低「react.js」と「react-dom.js」さえあればいい。

## JSX
React用の記法。テンプレートエンジン的な。

```jsx
var hoge = (<div>Hello, Mr. {name} </div>);
```
みたいな書き方が許される。
もちろんjsの記法だけでも以下のように

```js
var hoge = React.createElement("div", null, "Hello", name)......
```
みたいな書き方もできなくはないけどそんなことするくらいならJSXで書いてjsに変換したほうが早いと思う。

### JSXTransformer
配布されているJSXTransformer.jsを読み込めば、事前に変換しなくてもjsxファイルを読める。
手軽にHello world等を試すのに使える。
実際に動かすとなると後述のBabelなどで事前に変換するのが良いと思われる。

## npm
Node.jsのパッケージマネージャ。Node.jsをインストールすればだいたいついてくる。
Reactや以下のBabelやWebpackをインストールでき、更にインストールされたライブラリのコマンドも打てるようになる。

## Babel
"jsっぽい言語"（AltJS）のコンパイラ・トランスパイラ。
jsxみたいなjsっぽい言語のほか、ES（ECMA Script）等の「正規のjsだけど各ブラウザで未実装」みたいなのを「みんなが使えるちょっと古い時代のjs」に変換してくれる。
例えばES2015以前の（筆者が知っている）javascriptはクラスもアロー関数もないけれど、最近のESではちゃんと仕様が決まっているのでBabelで変換してしまえばクラスもアロー関数も書き放題。

## Webpack
モジュールバンドラ。
 モジュールバンドラは複数のjsファイルをまとめて一つのファイルにしてくれる。
クラスごとにファイルを分けたりしていても、Webpackで１個にまとめてしまえばサイトにアップロードするのが楽になる。おまけにHTTPの都合上複数ファイルがあるときより読み込みも早い。
更にWebpackはCSSやらフォントやらもまとめて一つにすることが可能。
各ファイルを読み込むときにローダーというプラグインを噛ませることができるので、例えばBabelでjsxを変換するみたいなこともまとめてできる。

似たようなものにbrowserifyやrollup、Gulpなどもあるけど、とりあえず今回はwebpackで話を進める。

## react-toolbox
React用UIライブラリ。

```jsx
(<div>
   <DatePicker />
</div>)
```
なんて書くだけで日付のピッカーを使えたりしてチョー便利。が、これを動かすまでがチョー大変だった。

## CSS
ちょっと前はスタイルを記述するだけだったのが、最近はモジュール化がどうこうとかで公式・非公式含めていろんな仕様がある。

#### CSS Modules
CSSモジュール。別なファイルなら同じセレクタ名を命名しても、変換時に接頭辞をつけて衝突しないようにしてくれる。これでモジュール化がしやすくなるということらしい。
これの変換をしないとセレクタ名が衝突するので変換しよう。

#### Sass
今回は使っていない。
SASS形式とSCSS形式の2通りの書き方があるスタイルシート。CSSに変換して使う。
ネスト機能などもあるが、そういった機能は後述するPostCSSでも代用できる。

## PostCSS
CSSのポストプロセッサ・プリプロセッサ。
いろんな機能が使用されたCSSを、各ブラウザが対応しているCSSに変換してくれる。
CSS内で変数を使ったり、CSSのネストができるようになったりする。（プリプロセス）
CSSに各ブラウザ用のプレフィックス（ベンダープレフィックス）を自動でつけてくれる機能もある（ポストプロセス）
名前の割に後処理以外にも事前処理も結構する。
#### CSS Color Module Level4
CSSの色に関する仕様。この機能がreact-toolboxには使用されているけどブラウザは未対応なのでPostCSSを使って変換してあげよう。

## webpack-dev-server
npmライブラリ。サーバーを起動してwebpackでまとめたjsをブラウザで確認できる。
ファイルの編集に合わせてオートリロードしてくれるので毎回ビルドする必要がなくなる。

# 各人の役割
思いの他大所帯になってしまった。
<img width="600" alt="react.png" src="https://qiita-image-store.s3.amazonaws.com/0/47220/bd45fdf8-74e6-37a9-1e63-315d840bc3fc.png">
*JSX*や特殊な*CSS*はそのままではブラウザで読めないので、**webpack**に渡してやる。
すると**webpack**は各種ツール(**Babel**や**PostCSS**など)にソースを渡すことで一般的なjs・cssに変換する。
最終的に変換されたファイルを**webpack**がひとまとめにして一つのjsに吐き出してくれる。
あとは**webpack-dev-server**でlocalhost上で確認したり、自分のサイトにアップしたりすればブラウザで見れるようになる。

# ハローワールド
react-toolboxのボタンにHello worldと書いて配置してみる。
作業ディレクトリで`npm init`と実行する。
npmはインストールしたパッケージを記録する`package.json`ファイルが必要で、質問に答えていくことでそれを作成してくれるコマンド。
`npm init`しなくてもname、version、description要素が記述された`package.json`という名前のJSONファイルがあれば良い。

自分が書くコードは

```html:dist/index.html
<html>

<head>
  <link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>

<body>
  <div id="app"></div>
</body>
<script src="./index.js"></script>

</html>
```

```jsx:src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {Button} from 'react-toolbox/lib/button';
ReactDOM.render((
  <Button label="Hello World!" raised primary/>
), document.getElementById('app'));
```
これくらい。

## パッケージの導入
上のコードを変換するのに使ったパッケージが

```json
    "@babel/cli": "^7.1.2",
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.4",
    "css-loader": "^1.0.0",
    "postcss-color": "^1.0.1",
    "postcss-loader": "^3.0.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.9"
    "@babel/polyfill": "^7.0.0",
    "npm": "^6.4.1",
    "postcss": "^7.0.5",
    "postcss-calc": "^7.0.0",
    "postcss-preset-env": "^6.2.0",
    "react": "^16.0.0",
    "react-dom": "^16.0.0",
    "react-toolbox": "^2.0.0-beta.13",
```
こんな感じ。

`npm install @babel/cli @babel/core --Save`のようなコマンドで作業ディレクトリにパッケージがインストールされる。
　依存関係のバージョンがおかしかったりするとちゃんとエラーメッセージが出るのでその時は表示されたバージョンを指定してインストールする。
　Babelは新しいバージョンは`@babel`という名前になっていて、以前のバージョンの`babel`パッケージもあるのでややこしい。
　`〇〇-loader`系のパッケージはwebpackのプラグイン。例えば`css-loader`はwebpackがcssをロードするときに使う。
　`postcss-〇〇`系はPostCSSのパッケージ。例えば`postcss-calc`はCSS内で`calc()`メソッドが使えるようになる。他にも`postcss-color`は色を変更する`color()`メソッドを標準のCSSでも実装されている`rgb()`メソッド等に置換してくれる。
react-toolboxは一部に`color()`や`calc()`などのPostCSSのメソッドを使用しているのでそのままだとうまく反映されない。

## 各パッケージの設定
### package.json
npmの設定はここに。
`script`欄にコマンドを書いておくと一発で呼び出せるようになる。例えば

```json:package.json
{
  //...
  "scripts": {
    "build": "webpack",
    "start": "webpack-dev-server"
  },
  "dependencies": {
//...
```
としておけば`npm run build`でwebpackを、`npm run start`でwebpack-dev-serverを起動できる。
npm 5.2以上なら`npx webpack-dev-server`のように直接パッケージを実行することも可能。

### webpack.config.js
webpackの設定はここ。

```js:webpack.config.js

module.exports = {
  mode: "development",
  entry: `${__dirname}/src/main.jsx`,

  output: {
    path: `${__dirname}/dist`, // distディレクトリに出力
    filename: 'index.js'
  },
  // webpack-dev-serverの設定
  devServer: {
    contentBase: 'dist', // distディレクトリを起動
    open: true // 自動でブラウザを開く
  },

  module: {
    rules: [
      // JSXファイルはbabelに渡して変換してもらう
      {
        test: /\.jsx/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
      // CSSはpostcss->css-loader->style-loaderの順に渡して変換してもらう
      {
        test: /\.css/,
        use: [
          'style-loader', // <style>タグに
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,  // SourceMapを生成してデバッグしやすくする
              importLoaders: 1, // css-loaderより前のローダの数（今回は１）
              modules: true,  // CSSモジュールに対応させる（大事）
            },
          },
          {
            loader: 'postcss-loader', // postcssの設定自体は別ファイル
            options: {
              sourceMap: true,
            }
          }
        ],
      },
      // Fonts
      {
        test: /\.(woff|eot|ttf)/,
        use: [
          'url-loader'
        ]
      }
    ]
  },

  resolve: {
    extensions: ['.js', '.jsx']
  },

  plugins: []
};
```
重要なのは`module.rules`以下。`test`の正規表現に当てはまるファイルを`use`に指定したプラグインに渡す。
`use`が複数の場合は、後ろに書かれたプラグインから順に適用される。
特にCSSファイルは

1. postcssで独自関数やネストなどを変換
2. css-loaderで**CSSモジュール
**を変換
3. style-loaderで変換したCSSを`<style>`タグに配置

という処理をしている。

### babelrc
babelの設定はここ。

```json:babelrc
{
  "presets": [
    "@babel/preset-env",
    "@babel/react"
  ]
}
```
Babelで使用するプリセットを選択。
`@babel/prese-env`は大体のES記法を変換してくれる。`@babel/react`でjsxなどのReactの文法を変換。

### postcss.config.js
postcssの設定はここ。

```js:postcss.config.js
module.exports = {
    plugins: [
        require('postcss-preset-env')({
            stage: 0, // 試験的な機能も含め変換
            features: {
                'custom-properties': {
                    preserve: false,  // カスタムプロパティを元の形式で保存しない
                },
                'color-mod-function': true, // color-mod()
            }
        }),
        require('postcss-calc'), // calc()
        require('postcss-color'), // color()
    ]
}
```

## 実行
`npm run build`または`npx webpack`でビルド。
`src/`フォルダのファイルがwebpackによっていろんなプラグインにたらい回されて`dist/`に出力される。
`npm run start`または`npx webpack-dev-server`でローカルにサーバーを立てて起動。
`dist/index.html`が開く。

うまく行けばボタンが表示される。うまく行かなければコンソールか、ブラウザの開発者コンソールにエラーがでているはず。

まぁちょっとこれだけだと労力に対して結果がショボい気もする。
でもほら、ボタンじゃなくて`<DatePicker />`とかにするとちょっとすごい。
![NoName_2018-10-25_6-14-9_No-00.png](https://qiita-image-store.s3.amazonaws.com/0/47220/b23186a9-1742-db10-d08a-98af1d1cf566.png)

## エラーが出る箇所
やたらめったらプラグインを使ったので色んな所からエラーが出る。

+ Node.jsとnpmはバージョンによる違いが結構ある。特にWindows版。
    + npmで最新のnpmをインストール（`npm install npm`）して古いnpmを削除……みたいな方法とかややこしい
+ まずWebpackはバージョン4を使っているので3以前の資料では異なる場合がある。
react-toolboxは依存関係のバージョンの警告が出たのでBeta版まで引き上げてみた。
    + よって[公式](http://react-toolbox.io/#/)より[GitHub](https://github.com/react-toolbox/react-toolbox)を見たほうがわかりやすい。
    + PostCSS周りでreact-toolboxのGitHubには「`color-mod()`を使っている」的なことも書かれていたけどBetaでは`color()`のほうが使われているような気がする。多分そういった変化がたくさんあるので都度調べる必要がある。
+ Babelやwebpackのエラーはメッセージを調べればそこそこ簡単にたどり着ける
+ CSSはエラーがメッセージとして出ることが少ないので`stylelint`のようなCSSのLintツールも導入すればわかりやすいかもしれない。

# まとめ
+ npmから始まり、React、Babel、webpack、CSS、PostCSS等々の愉快な登場人物についての理解を深める必要があった。
    + ちなみにReactはコマンド一発で環境を構築できるツールなんかもあるので手っ取り早くそれを使うのがいいんだろうか。
+ 一応動くところまで環境を構築できたので、あとはポチポチReactのコンポーネントを配置していくだけでWEBアプリが作れそう。


# 参考文献
+ [react-toolbox](https://github.com/react-toolbox/react-toolbox)
+ [最新版で学ぶwebpack 4入門 - スタイルシート(CSSやSass)を取り込む方法 - ICS MEDIA](https://ics.media/entry/17376)
+ [CSSモジュール ― 明るい未来へようこそ | POSTD](https://postd.cc/css-modules/)
+ [webpackでjsxファイルをJavaScriptにトランスパイルする - Qiita](https://qiita.com/ledsun/items/4a7fc4a2509d79bfa9a7)
+ [Reactで「Hello world」 – React入門 - to-R Media](https://www.to-r.net/media/react-tutorial02/)
+ [Create a New React App – React](https://reactjs.org/docs/create-a-new-react-app.html)
+ [これからReactを勉強する人が最初に見るべきスライド７選 | UX MILK](https://uxmilk.jp/43555)
+ [JSといえばjQueryだったWebデザイナーが、Reactを1年間使って感じたメリット | dwango creators' blog（ドワンゴクリエイターズブログ）](http://creator.dwango.co.jp/3413.html)
+ [色を自在に変換するcolor-mod()関数 - CSSにおける色の指定方法 | CodeGrid](https://app.codegrid.net/entry/2016-colormod-1)