flux 筆記 - 使用 react.js 與計數器範例

outline

  • 什麼是 Flux
  • Flux 簡介
  • Demo: 計數器

什麼是 Flux?

  • Design Pattern
  • 單向資料流
  • 利於大型 SPA 開發流程

MVC

Flux

Facebook Flux 流程圖


Flux 角色

  • Action
  • Dispatcher
  • Store
  • View
    • React

Action

  • Object
  • 定義所有 APP 事件
  • 可以快速掌握 App 所有的功能

Action example:

1
2
3
4
5
{
type: 'ADD_USER',
name: 'jacky',
age: '18'
}

Action creator?

  • 將 action 送給 dispatch

Action creator example:

1
2
3
4
5
6
7
var user = {
type: 'ADD_USER',
name: 'jacky',
age: '18'
}
AppDispatcher.dispatch( user );

Action creator example 改寫:

1
2
3
4
5
AppDispatcher.dispatch({
type: 'ADD_USER',
name: 'jacky',
age: '18'
});

Dispatcher

  • 統一向 Store 派送 action 事件機制
  • 只有一個

Store

  • 操作資料
  • 儲存資料
  • 發出事件,告訴所有人有資料改變

View

  • 接收事件
  • 重新取得資料
  • 根據資料改變 UI

用彈珠台舉例:

Flux 彈珠檯
Action 彈珠
Action Creators 彈簧拉把
Dispatcher 透過中間的釘子分類
Store 彈珠掉落的位子
View 得到的分數

如何改變分數?

  • Flux:
    • 發一個 Action!
  • 彈珠檯:
    • 打一顆彈珠!




Demo: 計數器

Dispatcher

加入 Dispatcher

1
window.App.AppDispatcher = new Flux.Dispatcher();

Action

設計計數器有兩個按鈕,所以有兩個 action 負責加1減1

type 是必要的 key ,之後到了 Store 用來分類處理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
window.App.CountActions = {
addClick() {
AppDispatcher.dispatch({
type: '加1',
value: 1
});
},
subClick() {
AppDispatcher.dispatch({
type: '減1',
value: -1
});
}
}

Store

負責將 Dispatcher 傳過來的 action 資料做處理

可以看到 AppDispatcher.register 裡面是一個 switch 分類各種 action type

接著分類到各自要處理的 function 中,回傳處理後的資料,改變 Store

因此 Store 的資料是自己操作改變的,

Store 可以說是一個閉包的概念 COUNT 則是私有數值

公開 getAll 讓大家取得 COUNT 數值

再提供 addChangeListener 讓需要的人註冊事件監聽

操作完 Store 資料會執行發布事件 _emitter.emit(CHANGE_EVENT);

接著再 view 那端就會接收到 'CHANGE' 事件,再回到 Store get COUNT

因此只要一直循環這個流程就可以建立出一套系統

每個元件只需要關注自己需要的資料既可

而資料統一集中在一個地方不會有被別人更改的情形

需要更改資料,必須建立一個 action 來統一管理所有功能

再透過 Dispatcher 將 action 丟給每個 Store 並根據 type 去改變相對應的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// event
const CHANGE_EVENT = 'CHANGE';
const _emitter = new EventEmitter();
// STROE
let COUNT = 0;
const addition = (count, value) => {
console.log(count + ' + ' + value);
return count + value
}
window.App.CountStore = {
getAll() {
return COUNT;
},
addChangeListener(callback) {
_emitter.on(CHANGE_EVENT, callback);
return () => _emitter.removeListener(CHANGE_EVENT, callback);
},
dispatchToken: AppDispatcher.register((action) => {
console.log('action:', action.type );
switch (action.type) {
case '加1':
COUNT = addition( COUNT, action.value );
_emitter.emit(CHANGE_EVENT);
break;
case '減1':
COUNT = addition( COUNT, action.value );
_emitter.emit(CHANGE_EVENT);
break;
}
})
};

View

在 react 元件內加入監聽:加在已經 Mount 階段,因為此階段,元件已經被產生,所以可以接收資料來改變元件。

react 生命週期 componentDidMount() 可以想像成 $( document ).ready(); 事件,必須要在 HTML 繪製完成後才可以對 element selector 並做修改

接著在 react 元件 將要卸載 階段,取消監聽事件。

1
2
3
4
5
6
7
8
9
10
11
class CountApp extends React.Component {
componentDidMount() {
this._removeChangeListener = CountStore.addChangeListener(
() => this.setState({ clickCount: CountStore.getAll() })
);
}
componentWillUnmount() {
this._removeChangeListener();
}

心得

長期就對 react/flux 有很高的興趣,可是單純看官網範例與部落格文章都不能明白 flux 流程與每位角色的負責工作。

這次趁導讀 flux 機會 run 幾次最間單的計數器與 todolist 範例就可以了解 flux Design Pattern 的奧妙之處,感覺這樣的機制真的很適合許多場景,因為 flux 類似於 observer 的 Design Pattern 所以就算是 angular 雙向綁定的框架,我想是可以混用在 api get / set 流程上,讓原本雜亂的 $scope 有了方向性,覺得很棒。

非常感謝 shiningjason1989 做了 react 每一個步驟的教材,並在文章中反覆的問你,真的了解了這章節的內容了嗎?非常棒的教材。

reference