React Flux 介紹與實作

Jim Kang
Fullstack Memory Dump
8 min readFeb 26, 2015

簡介

flux = event & data單向流,
用以下的圖來說

來演個戲:
View = 老饕,
Action = 服務生,
Dispatcher = 總舖師,
Store = 小廚師

老饕(view)訂了一份香蒜辣味海鮮墨魚麵 (LA PASTA),
服務生(action)告訴總鋪師 (dispatcher)老饕的訂單,
總鋪師(dispatcher)告訴廚師(們) (store)處理沙拉、濃湯、主餐、甜點,
廚師(們) (store)準備好就可以上菜了,
也就是說老饕的食物是從廚師準備來的。

有了這個概念我們就來開始我們的實作吧!

實作

(開始之前請使用 Mitch 的 react-boilerplate

我們來做一個使用者可以上線、下線的功能。
當使用者上線的時候會發個 token,
下線的時候會把 token 清除。

Component

getInitialState: function() {
return getStateFromStore();
},
...
function getStateFromStore() {
return {
status: StatusStore.getStatus(),
token: StatusStore.getToken()
}
}

這裏可知我們的 state 都是從 store 而來,
也就是說當 store 的值更新後,
component 也會跟著變更。

componentDidMount: function() {
StatusStore.addChangeListener(this._handleChange);
},
componentWillUnmount: function() {
StatusStore.removeChangeListener(this._handleChange);
},

這裏我們需要監聽 Store,
如果有變動,
我們會執行 _handleChange,
換句話說,
當廚師 (store) 的菜好了,
我們需要知道才可以趕快吃到香蒜辣味海鮮墨魚麵。

_handleChange: function(text) {
this.setState(getStateFromStore());
},

當新的資料準備好了也被通知到了,
我們會做 setState 的動作來變更畫面 (render),
於是我們從 store 那裏取的新的資料。

_setOnline: function(event) {
event.preventDefault();
StatusActions.online();
},
_setOffline: function(event) {
event.preventDefault();
StatusActions.offline();
},

這裏就是我們下訂香蒜辣味海鮮墨魚麵的動作,
呼叫 action 的 method 來處理指令。
StatusAction = 服務生
告訴他上線(online)或是下線(offline)

最後:

render: function() {
return (
<div>
<h3>Status: <code>{this.state.status}</code></h3>
<h3>Token: <code>{this.state.token}</code></h3>
<a href="#" onClick={this._setOnline}>Go Online</a>
<a href="#" onClick={this._setOffline}>Go Offline</a>
</div>
);
}

Action

StatusActions = {
online: function() {
AppDispatcher.handleViewAction({
actionType: 'ONLINE',
customProperty: 'game-on'
});
},
offline: function() {
AppDispatcher.handleViewAction({
actionType: 'OFFLINE',
customProperty: 'good-game'
});
},
};
module.exports = StatusActions;

action 其實很單純,
告訴 dispatcher 需要做的事情。
這裏你可以自己加入其他的 property,
就如總舖師會做主餐、甜點
AppDispatcher.handleViewAction => 總鋪師 . 主餐
AppDispatcher.handleServerAction => 總鋪師 . 甜點(不包含在此範例)
actionType => 香蒜辣味海鮮墨魚麵
customProperty => 辣度

變成:

服務生訂單 = {
套餐A: function() {
總鋪師.主餐({
名稱: '香蒜辣味海鮮墨魚麵',
辣度: '小辣',
備註: '加麵'
});
},
甜點B: function() {
總鋪師.甜點({
名稱: '焦糖布雷',
備註: '外帶'
});
},
};
module.exports = StatusActions;

Dispatcher

AppDispatcher 有配合一個主要的 dispatcher,
這個dispatcher會有兩個功能:dispatch & register
當有物件註冊(register)事件,
如果有 dispatch 的動作話就會被告知。
我們暫時先略過此 dispatcher 而先討論 AppDispatcher

AppDispatcher = assign({}, Dispatcher.prototype, {
handleViewAction: function(action) {
this.dispatch({
source: 'VIEW_ACTION',
action: action
});
}
});

source => ‘VIEW_ACTION’ 告知來源(例:總鋪師名字)
action => 也就是 action 的內容(例:餐點訂單內容)
this.dispatch 就是發佈命令(告知所有廚師)

Store

dispatcherIndex: AppDispatcher.register(function(payload) {
if(!payload.source === 'VIEW_ACTION') { return; }
var action = payload.action;
switch (action.actionType) {
case 'ONLINE':
if(status == 'OFF') {
token = StatusStore.generateToken();
}
status = 'ON';
StatusStore.emitChange('ON');
break;
case 'OFFLINE':
token = '';
status = 'OFF';
StatusStore.emitChange('OFF');
break;
}
return true; // No errors. Needed by promise in Dispatcher.
})

第一行的 AppDispatcher.register 也就是註冊 dispatcher的dispatch事件,
如果使用者重新上線後,
需要重新產出一個新的 token。
如果已經在線上就不需要了。
如果要下線,
需要清除目前的 token。

StatusStore.emitChange();

就是告訴大家菜準備好可以上了,

emitChange: function(data) {
this.emit(CHANGE_EVENT, data);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
this.removeListener(CHANGE_EVENT, callback);
},

這裏設定、監聽以及移除事件

getStatus: function() {
return status;
},
getToken: function() {
return token;
},

提供給 component 取得資料的 method

這樣我們的實作就差不多了。
會長的像這樣:

source code on git

flux 的慨念其實不難,
目前也有其他 flux 的工具,
例如 fluxxor, reflux, barracks 等。
幫忙把一些繁瑣的事情處理,
好方便使用。
若有任何問題及建議也歡迎提出!

--

--

Jim Kang
Fullstack Memory Dump

love writing bit sized programming memo, acoustic guitarist, proud daddy of 5 and great listener (to my kids)