【windows】【npm利用】Node.jsでreactを動かすお手軽環境を作成する

#記載していること
いつもjavaばっかり使ってるので、
最新っぽいreactjsがどういうものか使ってみたい！

ということでネットで色々な先人の知恵を借りつつ、Node.jsで組み込めるような開発環境を作ってみました。
簡単にNode.jsでreactを組み込んで開発できる環境の作り方を書いてみます。

ちなみにNode.jsもreactjsも今回初めて触ったので、、ご了承をば。

#今回利用するパッケージ一覧
+ express-generator
+ ect
+ react
+ react-bootstrap
+ browserify
+ reactify

~~node-jsx~~



Node.jsでっていいながらExpress使ってがっつり楽しています。
簡単に準備できる環境にしたかったので目を瞑って頂ければ！

#前提
・npmやNode.jsはインストール済みであること。
　※windowsに入れるときは多少躓きますが、ネットで検索すれば解決策がたくさん出てくるので割愛します。


#前準備
express-generator(express ver4系)を入れる
`npm install express-generator`

適当な場所でExpressで雛形作成する(今回はnode-react-sampleという名前)
`express ./node-react-sample`

#手順
##使うパッケージをインストールする
※★マークが必須です
`cd ./node-react-sample`

ect
テンプレートエンジン※高速らしいので
`npm install ect --save-dev`

★react
→本命
`npm install react --save-dev`

react-bootstrap
→bootstrapを使うなら
`npm install react-bootstrap --save-dev`

★browserify
→後々サーバーとクライアントでコード共有するために
`npm install browserify --save-dev`

★reactify
→browserifyするために必要
`npm install reactify --save-dev`

~~★node-jsx~~
~~→reactといえば、jsxで書くので。(直書きする人はそのままでOK)~~
~~`npm install node-jsx --save-dev`~~

##他もまとめてインストール
`npm install`
※jadeとか使わないのは、package.jsonから削除しといても良いかも。。

##構成に沿ってapp.jsを編集する
```js:/app.js
// ECTやbrowserifyとか読み込んでおく
var 
  express = require('express')
 ,app = express()
 ,path = require('path')
 ,favicon = require('serve-favicon')
 ,logger = require('morgan')
 ,browserify  = require('browserify')
 ,cookieParser = require('cookie-parser')
 ,bodyParser = require('body-parser')
 ,reactify    = require('reactify')
 ,ECT = require('ect');

// ページはindexのみ
var routes = require('./routes/index');

// ECTをテンプレートエンジンにしておく
app.set('views', path.join(__dirname, 'views'));
app.engine('ect', ECT({ watch: true, root: __dirname+'/views', ext: '.ect'}).render);
app.set('view engine', 'ect');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// indexページだけ
app.use('/', routes);

// reactのファイルをブラウザに渡す魔法の杖
app.get('/bundle.js', function(req, res){
  res.setHeader('Content-type', 'text/javascript');
  browserify('./template/indexForBrowser')
    .transform({ harmony: true }, reactify)
    .bundle()
    .pipe(res);
})

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;
```

##ectでテンプレートを作成する

```js:/views/layout.ect
<!DOCTYPE html>
<html>
  <head>
    <title><%= @title %></title>
    <script src="http://fb.me/react-with-addons-0.12.2.js"></script>
    <script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
    <link rel='stylesheet' href='/stylesheets/style.css' />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
  </head>
  <body>
    <% content %>
    <script id="initial-data" type="text/plain" data-json=<%= @initialData %>></script>
    <script src="/bundle.js"></script>
  </body>
</html>
```
initialDataってなんじゃい！という人、、、しれっと記載してすみません。
クライアント側のreactが初期に読み込むjsonデータです。
こういう構成嫌いという方はajaxで取得もできるので、後から色々編集してみてください。
→追記：せっかくサーバーサイドレンダリングしているので初期に読み込む必要ないかも知れません。

jqueryとか色々ここでとってますが、外しても良いです。。
あとJSX変換もリリースするときはこういうやり方ではなく、先に変換した方が良いのですが、
一旦、一旦開発環境なので。。。

bundle.jsはbrowserifyしたreactたちです。
これもリリース時は先に変換しておくと良いです。
その場合はapp.jsも編集しましょう。

さっきのは共通部分でこっちはindexページの中身です。

```js:/views/index.ect
<% extend 'layout' %>
<h1><%= @title %></h1>
<p>ユーザー情報管理画面</p>
<hr>
<div id="app">
<%- @userForm %>
</div>
```

※一応エラーテンプレート

```js:/views/error.ect
<h1><%= @message %></h1>
<h2><%= @error.status %></h2>
<pre><%= @error.stack %></pre>
```

##reactを使って簡単な入力欄を作成
reactが意味不明ー！っていうひと、とりあえず！呪文と思って書いてみて下さい！汗
templateフォルダを作ってreact系のファイル作成します。

```js:/template/index.js
var React = require('react');
var Bootstrap = require('react-bootstrap');

var BootInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value
    };
  }
 ,validationState: function() {
    var length = this.state.value.length;
    if (length > 10) return 'success';
    else if (length > 5) return 'warning';
    else if (length > 0) return 'error';
  }
 ,handleChange: function() {
    this.setState({
      value: this.refs.input.getValue()
    });
  }
 ,render: function() {
    return (
        <Bootstrap.Input
          type="text"
          value={this.state.value}
          placeholder="Enter Your Mail Address"
          label="メールアドレス"
          help="need more than 10bite."
          bsStyle={this.validationState()}
          hasFeedback
          ref="input"
          groupClassName="group-class"
          wrapperClassName="wrapper-class"
          labelClassName="label-class"
          onChange={this.handleChange}/>
    );
  }
});


var TestComp = React.createClass({
	getInitialState: function(){
		return {
			value: this.props.value
		};
	}
	,validationState: function(){
		var MAX = 20, thisSize = this.state.value.length;
		if (thisSize > MAX) return "Text length has exceeded the allowed size (20byte)";
	}
	,handleChange: function(){
	    this.setState({
			value: this.refs.input.getValue()
		});
	}
	,render: function(){
	    return (
	            <Bootstrap.Input
	              type="text"
	              value={this.state.value}
	              placeholder="20byte"
	              label="Memo"
	              help="keep your memo!"
	              bsStyle={this.validationState()}
	              hasFeedback
	              ref="input"
	              groupClassName="group-class"
	              wrapperClassName="wrapper-class"
	              labelClassName="label-class"
	              onChange={this.handleChange}/>
	    );
	}
});


var App = React.createClass({
	getInitialState(){
		return {
			//デフォルト
			 user: this.props.user
			,memo: "memo"
		};
	}
	,componentDidMount(){
		this.setState({
			//初期読込
		})
	}
	,render(){
		return (
      <div className="col-sm-3">
			 <BootInput value={this.state.user.email}/>
			 <TestComp value={this.state.memo}/>
      </div>
		);
	}
});

module.exports = App;
```

##クライアントでreactするjs作成
```js:/template/indexForBrowser.js
var React = require('react'),
    App   = require('./index')
;

var data = JSON.parse(document.getElementById('initial-data').getAttribute('data-json'));
React.render(<App user={data}/>, document.getElementById("app"));
```

##ページ表示処理をするjs作成
```js:/routes/index.js
var express = require('express');
var router = express.Router();


//server-side rendering
var React = require('react');
require('node-jsx').install({harmony:true});
var App = require('../template/index');

/* GET home page. */
router.get('/', function(req, res, next) {
	// 適当にデータを指定しておく
	var user = {
		email : "mix@mix.co.jp"
	};
	var json = JSON.stringify(user);
	res.render('index', 
		{ 
			title: 'Express', 
            // サーバー側でもreactで要素を作って、ectに取り込む
			userForm: React.renderToString(React.createElement(App, {user:user})),
		 	initialData: json
		}
)});

module.exports = router;
```
サーバー側でもreactで要素を作って、ectに取り込む、
っていう部分で一旦reactの要素としてhtmlを作成しています。
サーバーサイドレンダリングとか言うみたいですね。
reactには確かkeyとかあって、一度reactで作らないと差分比較レンダリングちゃんができないみたいです。

#起動してみる
`npm start`

#アクセスしてみる
http://localhost:3000/
にアクセス！！

![image](https://qiita-image-store.s3.amazonaws.com/0/70346/fe903dcf-2dfe-a901-7775-d78d9abb503f.png)

メールアドレスを短くすると、reactちゃんが動いていればエラーも出ます。
さあ！一旦簡単な環境は完成です！
色々書いてみてreactの世界を楽しみましょう！


#あとがき
初記事で「ちょっと何言ってるか分からない」状態になってしまったかもしれません。
動かない！とか意味不明！とかもしあればご指摘下さい。