読者です 読者をやめる 読者になる 読者になる

Hirooooo’s Labo

開発メモ、ガジェット、日記、趣味など、思った事を思ったまんま書くブログ

React × Redux 初心者入門(2日目:Material-UIを実装する)

f:id:hirooooo-lab:20160713002241j:plain

前回は環境構築から簡単なカウンターアプリをReact Reduxで作成してみました。 今回は更にMaterial-UIを追加してみたいと思います。

前回の記事 www.hirooooo-lab.com

公式サイト

MaterialUIの公式サイトは一通り見ておきましょう。

www.material-ui.com

material-uiのインストール

ターミナルから以下のnpmコマンドでパッケージをインストールします。 material-uiでreact-tap-event-pluginも使用してるので一緒にインストールしておきます。

$ npm install --save material-ui react-tap-event-plugin

injectTapEventPluginを追加する

material-uiでreact-tap-event-pluginを使用するので、index.jsxに以下のソースを追加します

import injectTapEventPlugin from 'react-tap-event-plugin';

injectTapEventPlugin()

なんか、そのうち使わなくても良くなるらしいですが、よく知らないです。。。すみません。
詳しくは公式サイトのこの辺りを参照

http://www.material-ui.com/#/get-started/installation

2016-10-11修正
import 定義が間違っていたのを修正しました。
指摘ありがとうございまっす!!

MuiThemeを読みこませる

テーマファイルの作成

material-uiで使用するテーマファイルを作成します。
公式サイトを診てもらうとわかるんですが、デフォルトでlightBaseThemedarkBaseThemeがあるようです。
ただ、今回は今後のためにじぶんでテーマファイルを作って読みこむようにしておきます。

src/myThemeFile.jsx

以下のsrc/myThemeFile.jsxを作成しましょう。

import * as Colors from 'material-ui/styles/colors';
import {fade} from 'material-ui/utils/colorManipulator';
import Spacing from 'material-ui/styles/spacing';

module.exports = {
  spacing: Spacing,
  fontFamily: 'Roboto, sans-serif',
  palette: {
    primary1Color: Colors.cyan500,
    primary2Color: Colors.cyan700,
    primary3Color: Colors.lightBlack,
    accent1Color: Colors.pinkA200,
    accent2Color: Colors.grey100,
    accent3Color: Colors.grey500,
    textColor: Colors.darkBlack,
    alternateTextColor: Colors.white,
    canvasColor: Colors.white,
    borderColor: Colors.grey300,
    disabledColor: fade(Colors.darkBlack, 0.3)
  }
}

App.jsxでテーマを読み込む

containers/App.jsxに以下のソースを追加します。

import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MyRawTheme from '../src/myThemeFile';

class App extends Component {
  static get childContextTypes() {
    return { muiTheme: PropTypes.object.isRequired };
  }

  getChildContext(){
    return {  muiTheme: getMuiTheme(MyRawTheme)};
  }

....( 省略)....

}

contextを使用する方法ですが、詳しくはこの記事が参考になります。

y-ukatama.hateblo.jp

テーマについても詳しくは公式のこの辺りを参照ください。
http://www.material-ui.com/#/customization/themes

これで準備は整ったので、実際にコンポーネントを変更してみます。

HeaderをAppBarにしてみる

Header.jsxにAppBarを追加する

前回のHeader.jsxにAppBarコンポーネントを追加してみます。
必要なコンポーネントのimportを追加して、前回の

      <header className="header">
        <h1 >ヘッダだYo</h1>
      </header>

の部分を

      <header className="header">
        <AppBar
          title="ヘッダだYO"
          iconElementRight={
            <IconButton>
              <FaceIcon />
            </IconButton>
          }
          showMenuIconButton={false}
        />
      </header>

こう変更します。

Header.jsxの全体像

import React, { PropTypes, Component } from 'react'
import { AppBar,IconButton  } from 'material-ui'
import  FaceIcon from 'material-ui/svg-icons/action/face'

class Header extends Component {
  render() {
    return (
      <header className="header">
        <AppBar
          title="ヘッダだYO"
          iconElementRight={
            <IconButton>
              <FaceIcon />
            </IconButton>
          }
          showMenuIconButton={false}
        />
      </header>
    )
  }
}
export default Header

実行してみる

ターミナルでnpm startを打ってみてください。

$ npm start

こんなページになったらOKです。
うん。素敵!

f:id:hirooooo-lab:20160715125109p:plain

ボタンをIcon Buttonにしてみる

ボタンの変更をする前に、containers/App.jsxに直接カウンターの部分が書いてあるので、コンポーネントに切り分けたいと思います。

components/counter.jsxの作成

components/Counter.jsxcontainers/App.jsxからカウンターの部分を抜き出します。

import React, { Component, PropTypes } from "react"

class Counter extends Component {
  static PropTypes = {
    value: PropTypes.number.isRequired,
    actions: PropTypes.object.isRequired
  }

  constructor(props, context) {
    super(props, context)
  }

  render() {
    const { value, actions } = this.props;
    return (
      <div>
        <h2>count={value}</h2>
        <button onClick={actions.increment}>増加</button>
        <button onClick={actions.decrement}>減少</button>
      </div>
    )
  }
}

export default Counter

ここで下記のようなpropsの型チェックの記述がありますが、

  static PropTypes = {
    value: PropTypes.number.isRequired,
    actions: PropTypes.object.isRequired
  }

staticの書き方をするためにbabelを少し修正します。

babel-preset-stage-1とbabel-plugin-transform-runtimeをインストール

npm install

ターミナルで以下のコマンドを実行

$npm install --save-dev  babel-preset-stage-1 babel-plugin-transform-runtime

.babelrcを修正

{
  "presets": ["es2015", "react", "stage-1"],
  "plugins": [
    "transform-runtime"
  ]
}

containers/App.jsxの修正

components/Counterをimportして、ボタンが直書きされていた箇所に追加します。

import Counter from '../components/Counter'
....(省略)....
render() {
    const { value, actions } = this.props;
    return (
      <div>
        <Header />
        <Counter value={value} actions={actions} />
      </div>
    )
  }
....(省略)....

valueとactionsはCounterコンポーネントのpropsに流し込んで上げましょう。

containers/App.jsxの全体像

import React, { Component, PropTypes } from "react"
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Header from '../components/Header'
import Counter from '../components/Counter'
import * as CounterActions from '../actions/counter'

import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MyRawTheme from '../src/myThemeFile';

class App extends Component {
  static get childContextTypes() {
    return { muiTheme: PropTypes.object.isRequired };
  }

  getChildContext(){
    return {  muiTheme: getMuiTheme(MyRawTheme)};
  }

  render() {
    const { value, actions } = this.props;
    return (
      <div>
        <Header />
        <Counter value={value} actions={actions} />
      </div>
    )
  }
}

App.propTypes = {
  value: PropTypes.number.isRequired,
  actions: PropTypes.object.isRequired
}

// Appコンポーネントにstateを流し込む
function mapStateToProps(state) {
  return state.counter
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(CounterActions, dispatch)
  }
}
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App)

実行してみる

npm startでさっきと変わらず表示されればOKです。

components/Counter.jsxの修正

いよいよIcon Buttonに変更してみたいと思います。

必要なコンポーネントをインポート

import IconButton from 'material-ui/IconButton';
import AddIconButton from 'material-ui/svg-icons/content/add-circle';
import RemoveIconButton from 'material-ui/svg-icons/content/remove-circle';
import {blue500, red500} from 'material-ui/styles/colors';

components/Counter.jsx全体像

import React, { Component, PropTypes } from "react"
import IconButton from 'material-ui/IconButton';
import AddIconButton from 'material-ui/svg-icons/content/add-circle';
import RemoveIconButton from 'material-ui/svg-icons/content/remove-circle';
import {blue500, red500} from 'material-ui/styles/colors';

const styles = {
  icon: {
    width: 48,
    height: 48
  },
  buttonSize: {
    width: 86,
    height: 86,
    padding: 24
  },
  countSize: {
    paddingLeft: 18
  }

};
class Counter extends Component {
  static PropTypes = {
    value: PropTypes.number.isRequired,
    actions: PropTypes.object.isRequired
  }

  constructor(props, context) {
    super(props, context)
  }

  render() {
    const { value, actions } = this.props;
    return (
      <div>
        <h2 style={styles.countSize}>count={value}</h2>
        <IconButton tooltip="増加"
          iconStyle={styles.icon}
          style={styles.blockSize}
          onClick={actions.increment}>
          <AddIconButton color={red500}/>
        </IconButton>
        <IconButton tooltip="減少"
          iconStyle={styles.icon}
          style={styles.blockSize}
          onClick={actions.decrement}>
          <RemoveIconButton color={blue500} />
        </IconButton>
      </div>
    )
  }
}

export default Counter

実行結果

こんな感じになりました

f:id:hirooooo-lab:20160715135819p:plain

まとめ

結構簡単にmaterial-uiが実装できると思います。
基本的に公式サイトに乗ってあるようなことしかやってないので、ちゃんと公式サイトを読めば問題なく使えるようになると思います。

次回予告

次回予告というか、あとやらなきゃいけないと思ってること

  • Materialデザインを実装する DONE!!
  • ページルーティングを実装する
  • REST通信を実装する
  • ログイン制御のフローを実装する
  • デプロイ方法を検討、実装する