React Tutorial Example (PostCSS)


React の Style Sheets は コンポーネントに設定すると効果的です。
また Style Sheets も *Hot Module Replacement* が利用できるので便利です。
[前回](http://qiita.com/ogomr/items/e6e702633c4f49a1893a) のアプリケーションに *PostCSS* を追加してみましょう。

## Learning

* Style Sheets の *Hot Module Replacement* を利用する。
* CSS Modules で *ローカルスコープ* を利用する。
* PostCSS で *SCSS* や *Sass* の機能を利用する。

## Environment

* node: v4.4.5
* npm: v3.9.6

## Comment Box Form

* 完成される [Source Code](https://github.com/ogom/react-comment-box-example/tree/postcss) のファイルリストです。

```bash
$ tree -a -I node_modules
.
├── .babelrc
├── app
│   ├── actions
│   │   └── index.js
│   ├── components
│   │   ├── Comment
│   │   │   └── index.js
│   │   ├── CommentBox
│   │   │   ├── index.js
│   │   │   └── style.css
│   │   ├── CommentForm
│   │   │   └── index.js
│   │   ├── CommentList
│   │   │   └── index.js
│   │   └── global.css
│   ├── containers
│   │   └── App
│   │       ├── index.js
│   │       └── style.css
│   ├── index.js
│   ├── reducers
│   │   └── index.js
│   └── store
│       └── index.js
├── index.html
├── package.json
└── webpack.config.js
```

*Let's hands-on*

### Setup application

* `git clone` コマンドでアプリケーションをダウンロードします。
* `npm install` コマンドで依存するモジュールをインストールします。

```bash
$ git clone https://github.com/ogom/react-comment-box-example.git
$ cd react-comment-box-example
$ git checkout convention
$ npm install
```

### Start HTTP Server

* `npm start` コマンドで *Webアプリケーション* を実行します。
* ブラウザで http://localhost:4000 をロードすると **Comment Box Example** が表示されます。

```bash
$ npm start
$ open http://localhost:4000
```

(API Server は [React Tutorial Example (Express)](http://qiita.com/ogomr/items/b90c68764c3c75cf501e) をご利用ください。)

### Remove link

* まず `index.html` から `<link />` の要素を削除します。

```diff:index.html
 <html>
   <head>
     <title>Comment Box Example</title>
-    <link rel='stylesheet' href='/style.css' />
   </head>
```

スタイルが適用されなくなりました。

![no_style](https://dl.dropboxusercontent.com/u/14690051/images/notes/lang/node/comment-box/no_style.png)

### Add CSS packages

* Stylesheet のモジュールをインストールします。

```bash
$ npm install style-loader css-loader --save-dev
```

### Add CSS loaders

* *Webpack* に `.css` を含めるように `loaders` を設定します。
* `.css` の拡張子を `import` で省略できるように `resolve` を設定します。

```diff:webpack.config.js
   module: {
     loaders: [
       {
         test: /\.js$/,
         exclude: /node_modules/,
         loaders: [
           'react-hot',
           'babel'
         ]
+      },
+      {
+        test: /\.css$/,
+        include: /app/,
+        loaders: [
+          'style',
+          'css'
+        ]
       }
     ]
   },
+  resolve: {
+    extensions: ['', '.js', '.css']
+  },
   devServer: {
     hot: true,
     port: 4000,
     inline: true,
     historyApiFallback: true
   }
 }
```

* コンテナに `style.css` を移動します。

```bash
$ mv style.css app/containers/App/.
```

* コンテナに `style.css` をインポートします。

```diff:app/containers/App/index.js
 import React, { Component } from 'react'
 import CommentBox from '../../components/CommentBox'
 import { connect } from 'react-redux'
 import { bindActionCreators } from 'redux'
 import * as Actions from '../../actions'
+import style from './style'
```

スタイルが再び適用されるようになりました。（HMRで表示されるので作業が捗ります。）

![on_style](https://dl.dropboxusercontent.com/u/14690051/images/notes/lang/node/comment-box/on_style.png)

しかし、これは *グローバルスコープ* の問題があります。
なので *ローカルスコープ* を使うようにしましょう。

### Add CSS modules

* *Webpack* に [CSS Modules](https://github.com/css-modules/css-modules) の `css?modules` を設定します。
    * [CSS Modules](https://github.com/css-modules/css-modules) はコンポーネントに CSS を設定することができます。

```diff:webpack.config.js
   module: {
     loaders: [
       {
         test: /\.js$/,
         exclude: /node_modules/,
         loaders: [
           'react-hot',
           'babel'
         ]
       },
       {
         test: /\.css$/,
         include: /app/,
         loaders: [
           'style',
-          'css'
+          'css?modules'
         ]
       }
     ]
   },
```

* `app/components/CommentBox/style.css` ファイルを作成します。

```bash
$ touch app/components/CommentBox/style.css
```

* コンテナの `h1` スタイルをコンポーネントに移動します。
* 移動したスタイルに `.normal` クラスを設定します。

```css:app/components/CommentBox/style.css
.normal h1 {
  border-bottom: 1px solid #ddd;
  font-size: 2.5em;
  font-weight: bold;
  margin: 0 0 15px;
  padding: 0;
}
```

* コンポーネントに `style.css` をインポートします。

```diff:app/components/CommentBox/index.js
 import React, { Component } from 'react'
 import CommentList from '../../components/CommentList'
 import CommentForm from '../../components/CommentForm'
 import $ from 'jquery'
+import style from './style'
```

* `className` に `style.normal` を設定します。

```diff:app/components/CommentBox/index.js
   render() {
     return (
-      <div className="commentBox">
+      <div className={style.normal}>
         <h1>Comments</h1>
         <CommentList data={this.props.data} />
         <CommentForm onCommentSubmit={this.handleCommentSubmit.bind(this)} />
       </div>
     )
   }
 }

 export default CommentBox
```

### Add PostCSS packages

* [PostCSS](http://postcss.org/) のモジュールをインストールします。

```bash
$ npm install postcss-loader autoprefixer precss postcss-import --save-dev
```

### Add PostCSS Import

* *Webpack* に [PostCSS](http://postcss.org) の `postcss` を設定します。
    * [PostCSS](http://postcss.org/) は [プラグイン](https://github.com/postcss/postcss/blob/master/docs/plugins.md) で *SCSS* や *Sass* の機能が使えます。

```diff:webpack.config.js
 var webpack = require('webpack')
+var postcssImport = require('postcss-import')
+var autoprefixer = require('autoprefixer')
+var precss = require('precss')

module.exports = {
   module: {
     loaders: [
       {
         test: /\.js$/,
         exclude: /node_modules/,
         loaders: [
           'react-hot',
           'babel'
         ]
       },
       {
         test: /\.css$/,
         include: /app/,
         loaders: [
           'style',
-          'css?modules'
+          'css?modules',
+          'postcss'
         ]
       }
     ]
   },
+  postcss(webpack) {
+    return [
+      postcssImport({
+          addDependencyTo: webpack
+      }),
+      autoprefixer,
+      precss
+    ]
+  },
```

* `app/components/global.css` ファイルを作成します。

```bash
$ touch app/components/global.css
```

* h1 の font-size を `$font-size-h1` の変数に設定します。

```css:app/components/global.css
$font-size-h1: 2.5em;
```

* `global.css` をインポートします。
* `font-size` に `$font-size-h1` の変数を設定します。

```css:app/components/CommentBox/style.css
@import '../global';

.normal h1 {
  border-bottom: 1px solid #ddd;
  font-size: $font-size-h1;
  font-weight: bold;
  margin: 0 0 15px;
  padding: 0;
}
```

これで `global.css` の値を編集すると *Comments* タイトルのフォントサイズが変更されます。

*Congrats!*

----

*スタイル* までもコンポーネントに分離できました。
次は [パッケージのアップデート](http://qiita.com/ogomr/items/224a1355078ea1a809a5) です。