追記:2016-01-20
改めて見たら冗長・間違ってた・アプデで動かないとかだったので、テスト書く勉強のついでにリファクタした。
下記は記録的にとりあえず残しておく。
目的
class などを使って書き換えてみる。
ファイル構成
.
├── dest
│ └── app.js
├── index.html
├── package.json
└── src
├── app.jsx
└── app_dispatcher.js
package
pacage.json
{
"devDependencies": {
"babelify": "^6.1.2",
"browserify": "^10.2.4",
"watchify": "^3.2.3"
},
"scripts": {
"watch": "watchify -t babelify src/app.jsx -o dest/app.js -v"
},
"dependencies": {
"flux": "^2.0.3",
"react": "^0.13.3"
}
}
$ npm install
$ npm run watch
code
src/app.jsx
import {EventEmitter} from 'events';
import React from 'react';
import AppDispatcher from './app_dispatcher';
const COUNTER_CONST = {
UPDATE_COUNTER: 'UPDATE_COUNTER'
};
// Store
let SINGLETON = Symbol();
let SINGLETON_ENFORCER = Symbol();
class CounterStoreSingleton extends EventEmitter {
constructor(enforcer) {
super();
if (enforcer !== SINGLETON_ENFORCER) {
throw "Cannot construct singleton";
}
AppDispatcher.register(this._onAction.bind(this));
this.counter = 0;
this.CHANGE_EVENT = 'change';
}
static get instance() {
if (!this[SINGLETON]) {
this[SINGLETON] = new CounterStoreSingleton(SINGLETON_ENFORCER);
}
return this[SINGLETON];
}
_onAction(action) {
switch(action.actionType) {
case COUNTER_CONST.UPDATE_COUNTER:
CounterStore.onUpdateCounter(action.count);
CounterStore.emitChange();
break;
default:
// no op
}
}
emitChange() {
this.emit(this.CHANGE_EVENT);
}
updateChangeListener(callback) {
this.on(this.CHANGE_EVENT, callback);
}
getCount() {
return {count: this.counter};
}
onUpdateCounter(count) {
this.counter += count;
}
}
let CounterStore = CounterStoreSingleton.instance;
// Action
let CounterActions = {
plusCounter: function() {
AppDispatcher.dispatch({
actionType: COUNTER_CONST.UPDATE_COUNTER,
count: 1
});
},
minusCounter: function() {
AppDispatcher.dispatch({
actionType: COUNTER_CONST.UPDATE_COUNTER,
count: -1
});
}
};
// View
class CounterAppView extends React.Component {
constructor() {
super();
this.state = CounterStore.getCount();
this._onChange = this._onChange.bind(this);
}
componentDidMount() {
CounterStore.updateChangeListener(this._onChange);
}
render() {
return (
<div>
<span>count: {this.state.count}</span>
<CounterView />
</div>
);
}
_onChange() {
this.setState(CounterStore.getCount());
}
}
CounterAppView.propTypes = {
count: React.PropTypes.number
};
class CounterView extends React.Component {
render() {
return (
<div>
<div>
<button onClick={this.onClickPlus}>+1</button>
<button onClick={this.onClickMinus}>-1</button>
</div>
</div>
);
}
onClickPlus() {
CounterActions.plusCounter();
}
onClickMinus() {
CounterActions.minusCounter();
}
}
React.render(
<CounterAppView />,
document.getElementById('app-container')
);
src/app_dispatcher.js
var Dispatcher = require('flux').Dispatcher;
module.exports = new Dispatcher();
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Flux</title>
</head>
<body>
<div id="app-container"></div>
<script src="dest/app.js"></script>
</body>
</html>
表示
$ python -m SimpleHTTPServer 8000
感想
- Store の class 化は下記を参考にインスタンスを返すようにした
- setState がうまく出来なくてハマったけど下記を参考にしたらできた
- EventEmitter を理解せずに書いてたら emit や on のところでハマりかけた。 下記をを参考にした。
- github の facebook/flux の example の AppDispatcher.js のコードが数か月前と書き方が変わりシンプルになっている。一部 Action に移動になってたり。なので解説記事があまり参考にならなかったりする...。
- react-mixin を使って LinkedStateMixin を使ってみようかと思ったけど断念した...