Day.1 Flux入門
React.jsは以前触っていましたが、Fluxのアーキテクチャについては不勉強だったため、
この機会に入門してみる。
まずはということで簡単なアプリを作ります。
今回勉強のために使わせていただいたのは下記のスライド↓(ありがとうございます)
私の方でもほぼ同一のものを作成しました。
アーキテクチャ
Fluxのアーキテクチャです。
React.jsだけを勉強していた私でも見たことがある有名な図ですね。
図から自明ですが、データの流れが一方向になっています。(Angularとかは双方向でしたよね)
Component
Viewですね。ここでAction(イベント)を呼び出します。
Store(state)が更新されたタイミングでViewが更新されます。
ユーザーは更新を意識する必要はありません。
Action
ここでは、システム的なイベントやユーザー入力(onClick等)等の管理を行います。
この後、Dispatcherへデータを渡します。
その他外部(API)との通信等も行います。
Dispatcher
DispatcherはFluxで提供されているモジュール。
Fluxの中ではコアな部分?
ここで、事前にStoreに登録されているコールバックを呼び出す。
本来はEventEmitterを内包しているのがDispatcherで、
EventEmitterの制御を行っているとのこと。
私自身EventEmitterは全然知らなくて、参考にしている記事からの情報しか知らないので
AdventCalendarのどこかで取り上げたい…
Store
Storeは基本的にデータの管理(state)を行います。
また、ビジネスロジックを記述する部分です。
またはStoreはSetterを持たず、コールバックにて、値の代入などを行います。(Getterはある)
処理に関して
私の理解で記述したファイルの処理を書きます。
自分のためなんじゃ…
※下記にプログラムを掲載しますが、参考記事様のコードとほぼほぼ同一なので
そちらを参照した方がよいかもしれません。
Component -> (Component.js)
今回作成したのは簡単なカウンターなので、
必要なViewは下記の2点のみです。
- カウントするボタン
- カウントを表示するラベル
constructor
- stateの初期値をStoreから取得
- Storeからのコールバックを登録(このタイミングで再描画)
tickメソッド
- Actionの呼び出し
"use strict"; import React from "react" import ActionCreator from "./ActionCreator" import Store from "./Store" import EventEmitter from "./EventEmitter" var dispatcher = new EventEmitter(); var action = new ActionCreator(dispatcher); var store = new Store(dispatcher); export default class Component extends React.Component { constructor(props) { super(props); this.state = { count: store.getCount() }; // Observe store's change store.on("CHANGE", () => { this._onChange(); }); } _onChange() { this.setState({ count: store.getCount() }); } tick() { action.countUp(this.state.count + 1); } render() { return ( <div> <button onClick={this.tick.bind(this)}>Count Up</button> <p> Count: {this.state.count} </p> </div> ); } }
Action -> (ActionCreator.js)
ここではcountUpメソッドが呼び出された際に、
Dispatcherにデータ全て横流ししています。
"use strict"; export default class ActionCreator { constructor(dispatcher) { this.dispatcher = dispatcher; } countUp(data) { this.dispatcher.emit("countUp", data); } }
Dispatcher -> (EventEmitter.js)
下記の指示を行うためのプロセスを登録
- カウンター値の更新
- Viewの更新
"use strict"; export default class EventEmitter { constructor() { this._handlers = {}; } on(type, handler) { if (typeof this._handlers[type] === 'undefined') { this._handlers[type] = []; } this._handlers[type].push(handler); } emit(type, data) { var handlers = this._handlers[type] || []; for (var i = 0; i < handlers.length; i++) { var handler = handlers[i]; handler.call(this, data); } } }
Store -> (Store.js)
??なぜEventEmitterを継承するんだろう…
constructor
onCountUpメソッド
Dispatcherから渡ってきたcountをStoreにて更新
画面を更新指示出しを行っている(emit)
"use strict"; import Emitter from "./EventEmitter" export default class Store extends Emitter { constructor(dispatcher) { super(); this.count = 0; dispatcher.on("countUp", this.onCountUp.bind(this)); } getCount() { return this.count; } onCountUp(count) { this.count = count; this.emit("CHANGE"); } }
さいご
使ってみてFluxの良さわかってません。
自分で考えて作成しようと思います。
ただ、一方向のデータバインディングなのでわかりやすいし、自分のタスクに集中出来る所がいいですね。