React
material-ui
jss

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を使うのが良さそうです。

前提条件

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

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

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

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

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

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

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

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

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

yarn start

開発用サーバーが立ち上がり、localhost:3000にブラウザからアクセスしてWelcome to Reactと表示されていれば成功です。
Screenshot from 2017-10-02 23-23-29.png

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

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

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

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

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

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

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

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系を次のようにパッケージ追加して共存させることができるので、順次移行作業を進めることができます。

# 既存のプロジェクトに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>内に追加することです。

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

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

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

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

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

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

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

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

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

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

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

  1. withStylesのimport

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

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

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

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

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

まとめ

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