這邊只會透過理解 Redux 的 API來簡介如何使用 Redux。這邊假設讀者都知道 web 中 event / listener 的 pattern。
如果想要知道 redux 背後的實作細節、原理、動機、Flux、Design Pattern、如何和 UI App 做連結,請看 Dan 的 Redux gitbook、Redux tutorial on EggHead。
redux 是一個狀態容器 (state container / state machine)
狀態容器要提供幾個功能
1 2 3 4 5 6
| 1. 初始容器狀態 _by Redux.createStore(**reducer**)_ 2. 取得現在的狀態 _by store.getState()_ 3. 狀態改變時,通知相關的程式 _by store.subscribe(**listener**)_ 3. 接收事件發生的通知、改變狀態 _by store.dispatch(**action**)_ 4. 建立之後改變事件處理方式 _by store.replaceReducer(**reducer**)_ 5. 由小的狀態容器組成全域狀態容器 _by 只能透過 combineReducers 來組成 root reducer,再用它來建全域狀態容器_
|
Flux 專有名詞:store、action、reducer。
store := 一個狀態容器
action := 一個像 Event 一樣有 type 屬性的 javascript object
reducer := 一個輸入 state 和 action、回傳新 state 的函數, (state, action) -> new_state
但 store 是 redux 中唯一一個狀態容器的名字。其實 API 等同於下方這樣子
1 2 3 4 5 6
| 1. _by Redux.createStore(**reducer**)_ 等於 _by Redux(**reducer**)_ 2. _by store.getState()_ 等於 _by Redux.getState()_ 3. _by store.subscribe(**listener**)_ 等於 _by Redux.addListener(**listener**)_ 3. _by store.dispatch(**action**)_ 等於 _by Redux.dispatch(**action**)_ 4. _by store.replaceReducer(**reducer**)_ 等於 _by Redux.replaceReducer(**reducer**)_ 5. _by 無直接 API,只能透過 combineReducers 來組成root reducer,然後用 root reducer 來建全域 store_
|
一個狀態容器 = 狀態 + reducer,但 reducer 中可以指定初始狀態、所以一個 reducer 其實可以定義一個狀態容器、等同於一個狀態容器,所以 API 等同於下方這樣子
1 2 3 4 5 6
| 1. _by Redux.initContainer(**reducer**)_ 等於 _by Redux(**container**)_ 2. _by Redux.getState()_ 3. _by Redux.addListener(**listener**)_ 3. _by Redux.dispatch(**action**)_ 4. _by Redux.replaceReducer(**reducer**)_ 等於 _by Redux.container.replaceReducer(**container.reducer**)_ 5. _by 只能透過 combineReducers 來組成 root reducer,再用它來建全域狀態容器_ 等於 _Redux.combineContainer_
|
把 combineContainer 用 addContainer 取代,API 可以改寫成這樣子
1 2 3 4 5 6 7
| 1. 初始容器狀態 _by Redux()_ 2. 取得現在的狀態 _by Redux.getState()_ 3. 狀態改變時,通知相關的程式 _by Redux.addListener(**listener**)_ 3. 接收事件發生的通知、改變狀態 _by Redux.dispatch(**action**)_ 4. 建立之後改變事件處理方式 _by Redux.getContainer(**name**).replaceReducer(reducer)_ 5. 由小的狀態容器組成全域狀態容器 _by Redux.addContainer(**container**)_ 6. 建立小的狀態容器 _by Redux.container(name, initialState, reducers)_
|
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
| let addReducer = (state, ADD_ACTION) => state++ let subtractReducer = (state, SUBTRACT_ACTION) => state--
counterContainer = Redux.container( 'counter', 0, [addReducer, substractReducer] )
Redux.addContainer(counterContainer)
class CounterComponent { constructor(props) { this.stateChangeListener = (state) => this.setState(state) Redux.addListener(this.stateChangeListener) } componentWillMount() { this.setState(Redux.getState().counter) } onAddButtonClick() { Redux.dispatch(ADD_ACTION) } onSubtractButtonClick() { Redux.dispatch(SUBSTRACT_ACTION) } ... }
|
總結,可以看出來 redux 最大的特點就是用 reducer 來定義 container state 的處理範圍,不能用 event callback 的概念去理解它,一個 reducer 對應到“一組” state、多個 actions。
所以在 Redux 中 reducer 差不多是 container state scope 等價。也因此在 Redux 中 sub-container / sub-store 的名字完全被省略了,你只能用 reducer 的名字來找到他們影子。只要知道這個規則大概就能夠理解 Redux 了。
reducer := (state, action) -> new_state (你找不到 container 的名字、它被省略了)
老實說,我覺得這樣子的開發者經驗很不舒服 (bad UX)
不知道 re-frame 的 UX 會不會比較好?