flux
そこら中で貼られてるfluxの概念図を貼っておきます。
簡単に言うと、
・Viewは親Component
・Actionは処理の内容を記したオブジェクト
・DispatcherはStoreへActionの通知を行う。具体的には登録されたCallbackを順次実行していく。
・Storeは状態の保持と状態の更新の通知を行う
View -> Action -> Dispatcher -> Storeとデータが伝搬されていく。
ViewはStoreを保持しており、Storeの状態が更新されたらViewに通知をする。
では、ひとつひとつの登場人物を詳しく見ていきます。
Dispatcher
DispatcherはActionを受け取って、登録されているCallbackを実行するものです。
Dispatcherに必要なメソッドはfluxライブラリから全て提供されていますので、私たちにに必要なことはDispatcherを使うことだけです。
使うというのは、DispatcherにCallbackを登録、Callbackを実行、Callbackが呼び出される順番を操作する、などといったことを指します。
import {Dispatcher} from "flux";
export default new Dispatcher();
Dispatcherはしばしば拡張されることがあります。
拡張にどのようなパターンがあるのかはわかりませんが、1つのパターンとして、どのVIewからdispatchされたか、という情報を付与するために用いられます。
import {Dispatcher} from "flux";
import assign from ‘object-assign’;
const dispatcher = assign(new Dispatcher(),{
handleViewAAction: function (action) {
this.dispatch({
source: "viewA",
action: action
})
},
handleViewBAction: function (action) {
this.dispatch({
source: "viewB",
action: action
});
}
});
export default dispatcher;
ViewからActionを実行するときは、dispatchメソッドの代わりに、先に拡張したメソッドを用います。
拡張していない場合は普通にdispatchメソッドを使います。
dispatcher.handleViewBAction(action);
こうすることで、どのViewからDispatchされたか、という情報を付与した上でCallbackを実行できます。
他にも拡張のパターンがあれば教えて下さい><
Action
Actionは処理の内容を記したオブジェクトをDispatcherにdispatch(発行)します。
dispatchされたらDispatcherは登録されたCallbackを実行する、という感じですね。
import dispatcher from "./dispatcher";
const Action = {
sampleAction(data){
dispatcher.dispatch({type:"SAMPLE_ACTION",value:data})
}
};
export default Action;
actionを受け取るCallbackはtypeを見てデータをどう扱うかを決定します。
Store
ストアは状態を保持します。
更に、dispatcherにCallbackを登録する役割もあります。
同じstoreが複数同じCallbackを登録してはいけないので、Storeはシングルトンになります。
import { EventEmitter } from "events";
import dispatcher from "./dispatcher";
class SampleStore extends EventEmitter {
constructor() {
super();
this.data = 0;
this.token = dispatcher.register(this.handleAction.bind(this));
}
getAll(){
return this.data;
}
emitChange(){
this.emit("change");
}
addChangeListener(callback){
this.on("change",callback);
}
handleAction(action) {
switch (action.type) {
case "SAMPLE_ACTION": {
this.data = action.value;
this.emit(‘change’);
}
}
}
}
export default new SampleStore();
コンストラクタでdispatcherにCallbackを登録しています。このメソッドにactionが届くわけですね。
さて、登録したCallback、handleActionにactionが届きます。
この時storeはactionの型を見て、どのようにデータを扱うかを判断します。
上記の例では単純にdataにactionのvalueを入れているだけですね。
そして、データに変更があったという事をEventEmitterにより通知しています。
この通知をViewが受け取って、状態が更新されていきます。
addChangeListener(callback){
this.on("change",callback);
}
例えば上記の例ですと、changeイベントが通知されればaddChangeListenerに登録されたcallbackが実行されるといった流れです。
View
Viewではまず、storeの初期値をコンストラクタで受け取ります。
一般にstoreではgetAllメソッドという名前で提供することが多いようです。
this.state = store.getAll();
そして、storeからデータの変更通知を受け取るcallbackを登録します。
let self = this;
store.addChangeListener(function(){
self.setState(store.getAll());
});
これで、storeの値が変更されると、viewのstatusも変更されるようになりました。
あとはこのstatusの値を子Componentのpropsとして流し込んでやれば単一方向のデータフローが実現できますね。