Material-UI使ってみた@VirtualBox（React+Redux+Express+Material-UI）

Material-UI使ってみた作業ログです。
目標は表題の組み合わせでエラー発生させずにMaterial-UIのコンポーネントを設置することです。

こんな感じになりました。
![スクリーンショット 2016-05-05 12.02.45.png](https://qiita-image-store.s3.amazonaws.com/0/66816/91789a96-69d1-d43f-ee66-9c1a222161a4.png)

# 環境作成
最近、環境は全部VirtualBox上に作っています。
ホストに作ってしまうと、しばらく使わない言語とか何入れてたっけ？となったり依存関係壊れてたりいろいろ面倒なのですが、VM上だと壊し放題なのが嬉しい。

参考サイト：
（というかほとんどそのままいただいたので、解説も読みたい場合はリンク先をおすすめ）
[Vagrant+node.js+express4+MongoDBで簡単なWebアプリを構築](http://webdesign-dackel.com/2015/09/29/vagrant-node-express4-mongodb/)

※以下VirtualBoxとVagrantのインストール済の前提。

## OSインストール
環境を作成するディレクトリに移動したら

> \$ vagrant init

でVagrantfileを作り、以下設定。

```lang:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
  config.vm.box = "puphpet/centos65-x64"
  config.vm.hostname = "node.local"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.synced_folder "localのworkspace", "/var/www/html"
end
```

仮想環境を立ち上げる。
（プラグインでhostsの書き換えを自動化するとかなり便利）

> \$ vagrant plugin install vagrant-hostsupdater
> \$ vagrant up

## 各種インストール
### nodeの設定

> \$ vagrant ssh
> \$ curl -L git.io/nodebrew | perl - setup
> \$ echo 'export PATH=\$HOME/.nodebrew/current/bin:\$PATH' >> ~/.bashrc
> \$ source ~/.bashrc
> \$ nodebrew install-binary v5.10.1
> \$ nodebrew use v5.10.1

node -vと打って、v5.10.1が表示されたら完了。

### nginxを設定

> \$ sudo yum install -y nginx
> \$ sudo service nginx start
> \$ sudo chkconfig nginx on
> \$ sudo vi /etc/nginx/conf.d/virtual.conf

virtual.confは以下のように設定。

```lang:virtual.conf
server {
  listen 80;
  server_name node.local;
  location / {
    proxy_pass http://127.0.0.1:3000;
  }
}
```

ここまででとりあえずの設定は終了なので、（ついでにmongoDBとかも入れた上で）Box化しておくと便利。

# Material-UIを使ってみる
## React+Redux+Expressな環境を作る
**参考サイト：**
1. [Material UIを使ってカッコいいUIのReactアプリケーションを作ってみた](http://blog.takanabe.tokyo/2015/12/11/1778/)
2. [React + Expressでのサーバーサイドレンダリング方法のまとめ](http://qiita.com/hmarui66/items/4f75e624c4f70d596873)

[サイト2のgithub](https://github.com/hmarui66/react-server-rendering-sample)のからソースをそのまま頂戴しました。
めちゃくちゃ助かりました。。

> \$ cd /var/www/html

で作業ディレクトリに移動してreact-router-reduxブランチのソースを落とします。
そのままnpm installすると落ちるので以下の作業を実施。

> \$ npm install -g rimraf
> \$ npm install isomorphic-fetch

Vagrantfileに設定したlocalのworkspaceに移動して、app/routes.jsxを以下のように修正。

```js:routes.jsx
// import Route from 'react-router';
import { Route } from 'react-router';
```

※以下のエラーに悩まされたときの解決策として、[こちら](https://github.com/reactjs/react-router/issues/1799)で発見しました。
*Warning: React.createElement: type sh
ould not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).*

## Material-UIを追加
### node_module追加
package.jsonに以下を追加します。

```json:package.json
  "dependencies": {
    "material-ui": "^0.14.4",
    "react-tap-event-plugin": "^0.2.0",
    "inline-style-prefixer": "^1.0.3",
```

※最初material-uiのバージョン0.13.4を入れてDatePickerを使うと以下のエラーが出たので検索したところ、バージョン固有の問題っぽかったので最新バージョンに上げた。
　*Invalid prop `onDismiss` of type `boolean` supplied to `Dialog`, expected `function`. Check the render method of `DatePickerDialog`*

作業完了したら、

> \$ npm install

でnode_moduleを追加・build

> \$ npm start

http://node.local/
にアクセス。無事表示されたら環境設定は完了。

### Material-UIを使う
ReduxとかReactについては追々ちゃんと勉強することにして、とりあえずMaterial-UIを動かす。
ディレクトリ構造をちょっと変更してimportのパスも変えておく。

> /app
> ├── components
> 　　├── **MainSection.jsx**・・追加
> 　　└── **Header.jsx**　・・追加
> ├── containers
> 　　├── App.jsx・・移動
> 　　├── Items.jsx・・移動
> 　　├── Users.jsx・・移動
> 　　└── **Material.jsx**　・・追加
> └── reducer

以下、追加変更内容。

```js:routes.jsx
import React from 'react';
import { Route } from 'react-router';

import App from './containers/App';
import Items from './containers/Items';
import Users from './containers/Users';
import Material from './containers/Material';

export default (
  <Route path="/" component={App}>
    <Route path="items" component={Items} />
    <Route path="users" component={Users} />
    <Route path="material" component={Material} />
  </Route>
);
```

```js:containers/Material.jsx
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Header from '../components/Header';
import MainSection from '../components/MainSection';

class Material extends Component {

  static propTypes = {
    onDismiss: React.PropTypes.func,
  };

  render() {
    return (
      <div>
        <Header />
        <MainSection />
      </div>
    );
  }
}

export default Material;
```

```js:components/Header.jsx
import React, { PropTypes, Component } from 'react';

import mui, {AppBar} from 'material-ui';
import themeDecorator from 'material-ui/lib/styles/theme-decorator';
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme';

class Header extends Component {

  render() {
    return (
      <header className="header">
        <h1>AppBar Component</h1>
        <AppBar title="React + Redux + Material UI Boilerplate" />
      </header>
    );
  }
}

export default themeDecorator(getMuiTheme(null, { userAgent: 'all' }))(Header);
```

```js:components/MainSection.jsx
import React, { Component, PropTypes } from 'react';

import mui, {CircularProgress,
             Tabs,
             Tab,
             DatePicker
            } from 'material-ui';
import themeDecorator from 'material-ui/lib/styles/theme-decorator';
import getMuiTheme from 'material-ui/lib/styles/getMuiTheme';

class MainSection extends Component {
  render() {
    return (
      <div>
        <h1>Progress Component</h1>
        <CircularProgress mode="indeterminate" size={1.5} />
        <CircularProgress mode="indeterminate" color={"red"} size={2} />
        <br/>

        <h1>Tab Component</h1>
        <Tabs>
          <Tab label="Tab One" value="0" />
          <Tab label="Tab Two" value="1" />
          <Tab label="Tab Three" value="2" />
        </Tabs>
        <br/>

        <h1>DatePicker Component</h1>
        <DatePicker hintText="Portrait Dialog" />
        <br/>
      </div>
    );
  }
}

export default themeDecorator(getMuiTheme(null, { userAgent: 'all' }))(MainSection);
```

```js:client.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router'
import configureStore from './configureStore';
import routes from './routes'
import injectTapEventPlugin from "react-tap-event-plugin";

const store = configureStore(window.APP_STATE);

//Needed for React Developer Tools
window.React = React;

//Needed for onTouchTap
//Can go away when react 1.0 release
//Check this repo:
//https://github.com/zilverline/react-tap-event-plugin
injectTapEventPlugin();

ReactDOM.render(
  <Provider store={store}>
    <Router history={browserHistory}>
      {routes}
    </Router>
  </Provider>,
document.getElementById('app'))
```

以下のエラーがなかなか消えなくて苦労したのですが、{ userAgent: 'all' }を入れることでとりあえず解消されました。

> warning.js:45 Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
 (client) th:100%;display:flex;min-height:64px;pad
 (server) th:100%;display:flex,-webkit-flex,-ms-fl

# おわりに
まだ何にも分かっていないけどスタートラインにはぎりぎり立てたので、いろいろ作りながら探っていきたいと思います。
とりあえずで以下は勉強しなきゃ。。

- [Redux入門【ダイジェスト版】10分で理解するReduxの基礎](http://qiita.com/kiita312/items/49a1f03445b19cf407b7)
- [『入門 React ―コンポーネントベースのWebフロントエンド開発』](http://www.amazon.co.jp/%E5%85%A5%E9%96%80-React-%E2%80%95%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AEWeb%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E9%96%8B%E7%99%BA-Frankie-Bagnardi/dp/4873117194)

# 追記
React+Redux+Material-UI、その後勉強しました。
この記事描いたころ何も理解していなかったなあと反省しきりです。。
罪滅ぼしに検索画面を作る入門記事とか書いたので、よかったら以下どうぞです。

[React + Redux入門④ - 検索画面を作成２：外観と動作をつくる](http://xiao-ctrl-y.com/2016/07/02/react-redux-begin-4/)