React/TS環境にemotion入れてみる

# はじめに

前回に引き続きReact/typescriptの環境構築やっていきます。
相変わらず拙い点が多いですがおおめに見てください。
前提となる環境は前回と同じです。

その１：　[React/TypeScript環境構築編](https://qiita.com/savacan/items/9e4e1bcab4f36a65a2e2)  

今回は、スタイル関係の気になったライブラリを導入していきます。
タイトルにある通りemotionなるものを試して見ることにします。

ひとまず今回の目標は簡単にスタイルを当てるところまでにします。


# 何故emotionを選んだか  

[emotion: introduction](https://emotion.sh/docs/introduction)

確たる理由はありません。
何やら後発のライブラリで、いろいろな書き方ができるそう。
折角いじるんだし、とりあえず使ったことが無いものを使ってみよう的なモチベーションです。

styled-componentsと比較してもそれなりに人気があることがわかります。

<img width="1254" alt="スクリーンショット 2019-06-15 21.59.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431787/580e04dc-9a78-53f2-7286-318dccd6fbad.png">

# emotion導入の前に１  

今回の導入は[その１](https://qiita.com/savacan/items/9e4e1bcab4f36a65a2e2)の続きから行っていきます。

ので、v0.1.0としてtagをつけて公開します。以下tag付けのコマンドです。
別にリリースするわけでもないのでブランチ切るだけでいいのですが、しっかり使ったことのない機能だったので使ってみます。

`$ git tag -a 'v0.1.0' -m 'react/typescript環境構築編'`
`$ git push origin v0.1.0`

手元で試したい方は、以下リポジトリをクローンしてv0.1.0タグをチェックアウトしてください。
`$ git clone https://github.com/savacan/sample-react-ts.git`
`$ cd sample-react-ts`
`$ git checkout -b (ブランチ名) refs/tags/v0.1.0`
`$ yarn install`


# babelを入れる  
以下の項で行うコマンド操作はプロジェクトルートです。

前回の環境構築編ではbabelを導入していませんでした。
どうやらドキュメントを読んでいる限り、babelを入れたほうが良さそうなので、導入します。

まず必要なものを入れていきます。
`$ yarn add -D @babel/core @babel/preset-env @babel/preset-react babel-loader`

次にbabelの設定ファイルを作ります。

`$ code .babelrc`

```.babelrc:.babelrc
{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}
```

次にwebpack.config.jsにbabel-loaderを使うように設定を入れます。
この時、loaderは下から実行されるそうなので、順番に注意してください。

```js:webpack.config.js
module.exports = {
 // 省略
   module: {
      rules: [
        {
          test: /\.tsx?$/,
          use: [
            {loader: 'babel-loader'},
            {loader: 'ts-loader'}
          ]
        }
      ]
    },
 // 省略
}

```

この状態で`yarn dev`で画面が見れたらbabel導入おっけーです。


# @emotion/core

reactで使う場合こいつを入れたらとりあえず使えそう。ということでまずドキュメントを読みます。

………

どうやら、css属性にstyleオブジェクトを指定する形で使うようです。
ひとまず導入して見ましょう。

まずinstallします。

`$ yarn add @emotion/core`

そして使ってみます。
componentディレクトリを作って、hogeコンポーネントを作って試してみます。

`$ mkdir src/component`
`$ code src/component/hoge.tsx`

### つまずきその１

早速インポートして………あれ？

<img width="710" alt="スクリーンショット 2019-06-16 0.37.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431787/92f08b9c-e2b9-90f1-7e62-d2f8cfdcd9ba.png">

ということで、少し調べてみました。
どうやらmodule解決のところで問題がおきていたようなので、moduleResolutionオプションをnodeにしてみると解決しました。これを機にtsconfig.jsonを以下のように変更。

```json:tsconfig.json
{
    "compilerOptions": {
        "outDir": "./dist",
        "sourceMap": true,

        "module": "ESNEXT",
        "target": "ESNext",
        "jsx": "react",

        "strict": true,
        "noUnusedLocals": true,
        "noImplicitReturns": true, 

        "moduleResolution": "node",

        "typeRoots": [
            "node_modules/@types"
        ]
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules"
    ],
}
```

＊ moduleResolutionオプションはデフォルトでnodeというような記述を見かけたのですが、明示的に書くことで変わるのでしょうか……?

### つまずきその２  

気を取り直して、hogeコンポーネントを書いていきます。

```tsx:hoge.tsx
/** @jsx jsx */
import * as React from 'react'
import {css, jsx} from '@emotion/core'

const Hoge: React.FC = () => {
    return (
        <div css={css({color: 'red'})}> Hoge is red! </div>
    )
}

export default Hoge;
```
そしてHogeコンポーネントを表示するようにindex.tsxを書き換えます。

```tsx:index.tsx
import * as React from 'react';
import { render } from 'react-dom';
import Hoge from './components/hoge';

const Main: React.FC = () => (
    <div>
        <div>Hello, Happy World!</div>
        <Hoge></Hoge>
    </div>
);

render(<Main />, document.getElementById('root'));
```

ひとまずこれで `$ yarn dev` すると、下の画面が表示されるはずです。

<img width="948" alt="スクリーンショット 2019-06-16 1.23.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431787/27c342f8-b2a5-88fe-53b4-ea2fa27fdcbe.png">

これで、一旦導入は出来ました。
しかし、このままだとコンポーネントの先頭行に毎回同じコメントを入れねばなりません。
ということで、`@emotion/babel-preset-css-prop`を導入してみます。

`$ yarn add -D @emotion/babel-preset-css-prop`

でインストールして。.babelrcに追記します。

```.babelrc:.babelrc
{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react",
        "@emotion/babel-preset-css-prop"
    ]
}
```

そして、hoge.tsxから先頭のコメントを外してみます。
すると、@emotion/coreからインポートしているjsxが未使用としてコンパイルで弾かれてしまいます。
ので、以下のようにまとめてインポートするように変更します。

```tsx:hoge.tsx
import * as React from 'react'
import * as emotion from '@emotion/core'

const Hoge: React.FC = () => {
    return (
        <div css={emotion.css({color: 'red'})}> Hoge is red! </div>
    )
}

export default Hoge;
```

これで画面を確認してみると………あれ？

<img width="948" alt="スクリーンショット 2019-06-16 1.31.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431787/b357a275-f274-820c-071e-859b96af6be6.png">

Hoge is red　が赤くなってないですね。
開発者ツールで確認してみると。

<img width="948" alt="スクリーンショット 2019-06-16 1.34.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431787/cf050441-2476-f0fd-bee2-5768eabca939.png">

css属性が解釈されていないですね。
どうやら、emotionのjsx関数ではなく、Reactの方でtsxを解釈しているようです。

これまで行った設定を冷静に思い出してみると、webpackさんが、まずts-loaderを呼び出して次にbabelさんを通るはず。なら問題はts-loaderか。ということで、もう一度tsconfigのドキュメントを読むことに。

どうやら、compilerOptionのjsxでreactを指定しているのが良くないようです。
ここでjsxを解釈してしまっているよう。
なので、jsxの解釈を後回しにするようjsxにpreserveを指定してみました。

<img width="948" alt="スクリーンショット 2019-06-16 1.45.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431787/f6d858db-c2aa-8811-cd71-be07d23f2cff.png">

うまくいきました……

ただ無知なだけですが、新しいものをいじるときはやっぱり時間がかかるなぁと。

そして、どうやらimportをまとめてやる必要もなさそうです。
emotion/babel-preset-css-propが、必要なところに自動でimportを入れてくれるようで、Hogeコンポーネントは以下のように書けます。

```tsx:hoge.tsx
import * as React from 'react';
import {css} from '@emotion/core';

const Hoge:React.FC = () => {
    return (
        <div css={css({color: 'red'})}>Hoge red!</div>
    )
}

export default Hoge;
```

# まとめ  

ここまでお疲れ様でした。

やっと@emotion/coreの導入だけが終わりました。
tscofing,webpack,babelの設定は本当にちゃんと勉強しようと反省しました……

今回はある程度長くなってきてしまったのでここまでにします。
次回はemotionを使って少しコンポーネントを書いたり、使い心地や挙動を確かめようと思います。

ここ間違ってるぞ、動かねぇぞ、という指摘お待ちしております。
以下リポジトリ、今回の進捗はadd-emotionブランチを切ってあります。

https://github.com/savacan/sample-react-ts/tree/add-emotion