React 状態によってデザインの変わるボタンコンポーネント作成（CSS Module, classnames）

:calendar_spiral: i-plug Advent Calendar 2019 の【19日目】の記事です:santa::tada:

はじめまして、i-0701 です:boy::boy_tone4::boy_tone1::boy_tone2::boy_tone3:

この度 React を触ることになりまして、まずはボタンのコンポーネントを作成しよう！と思ったので、その備忘録を残します。
私が css まわりから作成していくタイプのため、今回は **style 表示切り替え特化** です。（イベント処理に関しては今回書いておりません:cry:）

ちなみに使うものは
- CSS Module 
- [classnames](https://www.npmjs.com/package/classnames#usage-with-reactjs)

**CSS Module ？**
css で名前空間設定できるようなやつです。スコープしぼって使えるやつです。
1 個の css 修正してあそこもここも影響でた！！がなくて私は好きです。
共通なデザインの調整 css いっぱいやと、複数箇所修正する懸念がないこともないですが、コンポーネントなどはそれ特化の css するので問題ないはず。（最終、ここの margin は共通！って場合、sass で変数持ったらいいかもしれない）
あと、class名を考える時間が削減するので好きなのもあります。

## create-react-app
node 動く状態で（私の環境は `v10.13.0`）、create-react-app します。
今回は使いませんが、typescript 好きなので入れてます。

```bash
$ npx create-react-app ugoke --typescript
# npx create-react-app my-app --template typescript のが今正しかった
```
create-react-app した直後だと、webpack の設定だとかが見れないので、npm run eject 実行。

```bash
$ cd ugoke
$ npm run eject
```

## コンポーネントと style.module.scss つくる
src 以下に Button コンポーネントを作成。
scss 動かしたいので、node-sass いれます。

```bash
$ npm install node-sass
```

あとは `.module.scss` で作成すると勝手に create-react-app さんがええ感じにしてくれます。（CSS modules とかのモジュール入れなくても動く）

```
src
├── App.css
├── App.test.tsx
├── App.tsx
└── components 
  └── Button
    ├── Button.tsx 
    └── style.module.scss ← .module.scss にすること！
```

```typescript:Button.tsx
import React from 'react';
import styles from './style.module.scss';

// ちょっとの typescript 要素
interface Button {
  text: string,
}

function Button(props: Button) {
  return (
    <button className={styles.button} onClick={props.event}>
      {props.text}
    </button>
  );
}

export default Button;
```

```scss:style.module.scss
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center; // color とか変数として持つほうがいいけど今回はしないです。
  font-size: 14px;
  background: #F1F1F1;
  color: #333;
  border-radius: 4px;
  border: 1px solid #BFBFBF;
  padding: 6px 12px;
  cursor: pointer;
}
```

```typescript:App.tsx
import React from 'react';
import './App.css';
import Button from './components/Button/Button';

const App: React.FC = () => {
  return (
    <div className="App">
      <Button text="ボタン" />
    </div>
  );
}

export default App;
```

表示されました:blush:
![スクリーンショット 2019-12-13 16.24.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538891/8f9260b6-a491-b602-ff2c-202878af2467.png)

### ちなみに
> あとは `.module.scss` で作成すると勝手に create-react-app さんがええ感じにしてくれます。（CSS modules とかのモジュール入れなくても動く）

`npm run eject` してでてきた `config/webpack.config.js` を見ると、サポートしてるよって書いてくれてます。
今回だとの 457~490 行目あたりです。create-react-appさまさま:pray:

```js:config/webpack.config.js
            // Opt-in support for SASS (using .scss or .sass extensions).
            // By default we support SASS Modules with the
            // extensions .module.scss or .module.sass
            {
              test: sassRegex,
              exclude: sassModuleRegex,
              use: getStyleLoaders(
                {
```

## props によって style を変更する
今回は props によって見た目を disabled できるようにしてみます。

まずは複数の class 扱うのに便利な [classnames](https://www.npmjs.com/package/classnames#usage-with-reactjs) を入れます。


```bash
$ npm install classnames --save
# 「型がない」って怒られるので入れます
$ npm install @types/classnames
```

CSS Module を使ってるので[こちら](https://www.npmjs.com/package/classnames#alternate-bind-version-for-css-modules-)参照してコンポーネントを修正

```typescript:Button.tsx
import classNames from 'classnames/bind';

interface Button {
  text: string,
  disabled?: boolean,
}

let cx = classNames.bind(styles);

function Button(props: Button) {
  let buttonClass = cx({
    button: true,  // ベースの class
    disabled: props.disabled,
  });

  return (
    <button className={buttonClass}>
      {props.text}
    </button>
  );
}
```

disabled 用の class を書きます。

```scss:style.module.scss
.button {

  // 略

  padding: 6px 12px;
  cursor: pointer;
  
  &.disabled {
    cursor: default;
    opacity: .65;
  }
}
```


App で呼びます。

```typescript:App.tsx
const App: React.FC = () => {
  return (
    <div className="App">
      <Button text="ボタン" />
      <Button text="ボタン" disabled={true} />
    </div>
  );
}
```

表示されました:blush:
左が通常ボタン、右が disabled なボタンです。
![スクリーンショット 2019-12-18 18.43.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538891/37919332-20d5-f85f-f19e-d8e93fa3ab96.png)


## おまけ :yum: 失敗コードと感想
最初 CSS Module で以下みたいな書き方が分からず、

```tsx
  var btnClass = classNames({
    btn: true,
    'btn-pressed': this.state.isPressed,
    'btn-over': !this.state.isPressed && this.state.isHovered
  });

  // これだと動かない
  var btnClass = classNames({
    'styles.button': true,
    'styles.disabled': props.disabled,
  });
```
こんな↓書き方してしまったんですが……公式を読むとちゃんと書いてました:bow_tone2:

```tsx
  const classNameForButton = () => {
    if (props.disabled) {
      return ClassNames(styles.button, styles.disabled);
    }
    return ClassNames(styles.button);
  };
```

<br>
**公式 Docs をちゃんと読もうって思いました:bangbang::bangbang::bangbang:**