Material-UI v1.x (beta) を導入する

# はじめに

Reactで開発するときに手軽に使えるReactコンポーネントセットの中で特に人気の高いMaterial-UIの開発がv1.xのbetaになっており、いよいよ正式にリリースされようとしています。

そこで、beta版を導入してv1.xを先取りしてみます。

また、React本体も無事にライセンス問題が解決してv16のリリースがされたばかりなので、v16を前提とします。

## やること
- Material-UIを使った開発環境構築
- 簡単なButtonサンプル
- JSSを使ってスタイルを適用する

## 補足
server-side renderingを行う場合については触れていませんが、設計として考慮されていて、実現のためには[Next.js](https://github.com/callemall/material-ui/tree/v1-beta/examples/nextjs)を使うのが良さそうです。

## 前提条件

- Reactの基本的な理解
- npm, yarnで構築する開発環境の理解

## 開発環境の構築1 - create-react-appで土台を作る

既に開発環境が構築済みの場合、この項は飛ばしてください。

スムーズに開発環境を構築したいので、`create-react-app`を使います。

まだコマンドが登録されていない場合はインストールしましょう。

```sh
# create-react-appコマンドをインストール
yarn global add create-react-app
```

プロジェクトを作っていきます。

```sh
create-react-app my-mui-project
cd my-mui-project
```

無事に動作するか確認します。

```sh
yarn start
```

開発用サーバーが立ち上がり、localhost:3000にブラウザからアクセスして**Welcome to React**と表示されていれば成功です。
![Screenshot from 2017-10-02 23-23-29.png](https://qiita-image-store.s3.amazonaws.com/0/193814/bccca5a2-c6a6-bbf3-25f0-5e76de5eb250.png)


## 開発環境の構築2 - Material-UIの追加

Material-UIをプロジェクトに追加します。

SvgIconを使いたい場合は、v1.xから別のパッケージに分離されたので、そちらも追加します。

```sh
# beta版を試すには@nextを指定
yarn add material-ui@next -S

# SVGIconを使う場合はこちらも
yarn add material-ui-icons -S

```

この時点での`package.json`の中身

create-react-appのおかげで、とてもすっきりしています。

```json:package.json
{
  "name": "my-mui-project",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "material-ui": "1.0.0-beta.13",
    "material-ui-icons": "^1.0.0-beta.14",
    "react": "^16.0.0",
    "react-dom": "^16.0.0",
    "react-scripts": "1.0.14"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}
```
### 開発中版 v0.x を既に使っている場合

v1.x系を次のようにパッケージ追加して共存させることができるので、順次移行作業を進めることができます。

```sh
# 既存のプロジェクトにv1.xを追加
yarn add material-ui-next@npm:material-ui@next -S
```

v1.x系に置き換えたい部分では以下のようにimportを書きます。
```js
import FlatButton from 'material-ui/FlatButton'; // v0.x
import Button from 'material-ui-next/Button'; // v1.x
```

## Robotoフォントの追加

Material-UIはRobotoフォントを使用するようにデザインされていますが、RobotoフォントはMaterial-UIを追加しただけでは追加されません。

Robotoフォントの追加方法はいくつかありますが、一番簡単なやり方はCDNのリンクを`<head></head>`内に追加することです。

```html
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
```

## コンポーネントを使ってみる

さっそく`App.js`を書き換えて、コンポーネントを使ってみましょう。

UIコンポーネントの基本ということで、いろいろなボタンを追加してみました。

```js:App.js
import React, { Component } from 'react';
import Button from 'material-ui/Button';
import IconButton from 'material-ui/IconButton';
import AddIcon from 'material-ui-icons/Add';
import ModeEditIcon from 'material-ui-icons/ModeEdit';
import AddShoppingCartIcon from 'material-ui-icons/AddShoppingCart';
import DeleteIcon from 'material-ui-icons/Delete';

class App extends Component {
  handleClick = ()=> {
    alert("Clicked!");
  }

  render() {
    return (
      <div>
        <div>
          {/* クリックイベントの処理はこんな感じ */}
          <Button onClick={this.handleClick}>Default</Button>
          <Button color="primary">Primary</Button>
          <Button color="secondary"><DeleteIcon />削除</Button>
        </div>
        <div>
          <Button variant="raised">Default</Button>
          <Button variant="raised" color="primary">Primary</Button>
          <Button variant="raised" color="secondary"><DeleteIcon />削除</Button>
        </div>
        <div>
          <Button variant="fab" color="primary" aria-label="add">
            <AddIcon />
          </Button>
          <Button variant="fab" color="secondary" aria-label="edit">
            <ModeEditIcon />
          </Button>
        </div>
        <div>
          <IconButton aria-label="Delete">
            <DeleteIcon />
          </IconButton>
          <IconButton color="primary" aria-label="Add to shopping cart">
            <AddShoppingCartIcon />
          </IconButton>
        </div>
      </div>
    );
  }
}

export default App;
```

見た目はこんな感じになりました。
![Screenshot from 2017-10-03 01-54-28.png](https://qiita-image-store.s3.amazonaws.com/0/193814/8f281f07-b18d-4cb2-31f8-e1237a3bc254.png)

**[補足]**
v0.xのときは`Button`は`FlatButton`, `RaisedButton`などのコンポーネントに分割されていたのですが、同じ`Button`コンポーネントに`raised`などの属性を付ける形になりました。

## コンポーネントのスタイリング

いまの形は基本形で、CSSによるスタイルが全くありません。

スタイルの適用は`CSS modules`, `styled-components`などでも良いのですが、Material-UI v1.xからは[JSS](http://cssinjs.org)を内部のスタイル適用方法として採用しています。

ここでは公式に合わせてコンポーネント利用側でもJSSを使ってスタイルを追加していきましょう。

スタイル追加の手順は以下の通りです。

1. `withStyles`のimport  
    
    ```js
    import { withStyles } from 'material-ui/styles';
    ```
2. スタイルを定義する

    ```js
    const styles = { /*スタイル*/ };
    ```
3. コンポーネントを`withStyles`でラップする  

    ```js
    class MyComponent extends React.Component { ... }  

    export default withStyles(styles)(MyComponent);
    ```
4. 定義したスタイルが`props.classes`として渡されるので、`className`に適用する

ちょっとしたスタイルの適用後は以下。

```js:App.js
import React, { Component } from 'react';
import Button from 'material-ui/Button';
import IconButton from 'material-ui/IconButton';
import AddIcon from 'material-ui-icons/Add';
import ModeEditIcon from 'material-ui-icons/ModeEdit';
import AddShoppingCartIcon from 'material-ui-icons/AddShoppingCart';
import DeleteIcon from 'material-ui-icons/Delete';

import { withStyles } from 'material-ui/styles'; //追加

const styles = {
  box: {
    margin: 10,
    padding: 10,
    border: "solid 1px gray",
  },
  button: {
    margin: 10,
  },
  buttonWithHover: {
    margin: 10,
    // hoverも記述できる
    '&:hover': {
      backgroundColor: '#ff0000',
    }
  },
};

class App extends Component {
  handleClick = ()=> {
    alert("Clicked!");
  }

  render() {
    // 長くなるので参照
    const classes = this.props.classes;
    return (
      <div>
        <div className={classes.box}>
          {/* クリックイベントの処理はこんな感じ */}
          <Button onClick={this.handleClick} className={classes.button}>Default</Button>
          <Button color="primary" className={classes.buttonWithHover}>Primary</Button>
          <Button color="accent"><DeleteIcon />削除</Button>
        </div>
        <div className={classes.box}>
          <Button raised className={classes.button}>Default</Button>
          <Button raised color="primary" className={classes.button}>Primary</Button>
          <Button raised color="accent" className={classes.button}><DeleteIcon />削除</Button>
        </div>
        <div className={classes.box}>
          <Button fab color="primary" aria-label="add" className={classes.button}>
            <AddIcon />
          </Button>
          <Button fab color="accent" aria-label="edit" className={classes.button}>
            <ModeEditIcon />
          </Button>
        </div>
        <div className={classes.box}>
          <IconButton aria-label="Delete" className={classes.button}>
            <DeleteIcon />
          </IconButton>
          <IconButton color="primary" aria-label="Add to shopping cart" className={classes.button}>
            <AddShoppingCartIcon />
          </IconButton>
        </div>
      </div>
    );
  }
}

export default withStyles(styles)(App);

```
スタイルを適用して見た目を整えてあげると、こんな感じになりました。
![Screenshot from 2017-10-03 01-53-35.png](https://qiita-image-store.s3.amazonaws.com/0/193814/007df350-6da8-059e-2bbd-807b796ae322.png)

## まとめ
非常に簡単に、よくある見た目のWebコンポーネントを使うことができます。