# Redux 常見問題: 設計哲學
## 目錄
- [為什么 Redux 不把 state 和 action 傳給訂閱者?](#does-not-pass-state-action-to-subscribers)
- [為什么 Redux 不支持 class 形式的 action 和 reducer?](#does-not-support-classes)
- [為什么 middleware 簽名是柯里化的形式?](#why-currying)
- [為什么 `applyMiddlewrae` 要為 `dispatch` 創建一個閉包?](#closure-dispatch)
- [在 `combineReducers` 調用其所有 reducer 時,為什么不引入第三個參數來表示整個 state?](#combineReducers-limitations)
- [為什么 `mapDispatchToProps` 中不允許使用 `getState()` 或 `mapStateToProps()` 的返回值?](#no-asynch-in-mapDispatchToProps)
## 設計哲學
<a id="does-not-pass-state-action-to-subscribers"></a>
### 為什么 Redux 不把 state 和 action 傳給訂閱者?
訂閱者(subscribers)的意圖是響應 state,而不是 action。對 state 的更新是同步的,但給訂閱者的通知可能是批處理(batched)或節流(debounced)的,也就是說每次 action 發出時,訂閱者不一定立刻得到通知。這是一種常見的避免重復渲染所做的 [性能優化](http://cn.redux.js.org/docs/faq/Performance.html#performance-update-events)。
使用 enhancer 來替換 `store.dispatch` 是可以覆蓋批處理或節流行為的,從而改變通知訂閱者的方式。同時,有些類庫可以讓 Redux 成批處理 action 來優化性能的同時避免重復渲染:
- [redux-batch](https://github.com/manaflair/redux-batch) 允許向 `store.dispatch()` 傳遞一個由 action 組成的數組,包含的這些 action 只發送一個通知。
- [redux-batched-subscribe](https://github.com/tappleby/redux-batched-subscribe) 允許 dispatch 后成批地給訂閱者發送通知。
這是為了保證 Redux 最終調用所有訂閱者時使用最新的 state,但并非在每次 action 發出后調用所有訂閱者。在訂閱者中你很容易得到 store 當前的 state,只需調用 `store.getState()` 就行了。而在訂閱者中你卻沒法得到 action,因為多個 action 可能會被成批處理。
在訂閱者中使用 action 有一種可能的使用場景(當然這個特性并不支持),就是為了保證組件只在特定 action 發出后重新渲染。而對于重新渲染的控制應使用以下方法代替:
1. [shouldComponentUpdate](https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate) 生命周期函數
2. [virtual DOM 相等性檢查 (vDOMEq)](https://facebook.github.io/react/docs/optimizing-performance.html#avoid-reconciliation)、
3. [React.PureComponent](https://facebook.github.io/react/docs/optimizing-performance.html#examples)
4. 使用 React-Redux:用 [mapStateToProps](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) 訂閱組件時,只訂閱它需要的那部分 store。
#### 更多信息
**文章**
- [How can I reduce the number of store update events?](https://cn.redux.js.org/docs/faq/Performance.html#performance-update-events)
**討論**
- [#580: Why doesn't Redux pass the state to subscribers?](https://github.com/reactjs/redux/issues/580)
- [#2214: Alternate Proof of Concept: Enhancer Overhaul -- more on debouncing](https://github.com/reactjs/redux/pull/2214)
<a id="does-not-support-classes"></a>
### 為什么 Redux 不支持 class 形式的 action 和 reducer?
Redux 使用函數(稱為 action 創建函數)來返回一個 action 對象的模式,在某些有著豐富面向對象開發經驗的程序員眼中,似乎顯得有悖常理。他們覺得這是這顯然是類(class)和實例(instance)的使用場景。然而 Redux 不支持類的實例作為 action 對象和函數,這是因為類的實例會使得序列化(serialization)和反序列化(serialization)這樣的工作更加棘手。如 `JSON.parse(string)` 這樣的反序列化方法返回的是一個普通 JavaScript 對象而不是一個類的實例。
正如 [組織 State](https://cn.redux.js.org/docs/faq/OrganizingState.html#organizing-state-non-serializable) 所描述的那樣,如果你不需要像數據持久化、時間旅行調試這樣的功能,那么完全歡迎把不可以序列化的數據放入 Redux 的 Store 中存儲。
序列化可使瀏覽器使用更少的內存,儲存之前所有被 dispatch 的 action,以及 store 中所有以前的 state。時間回溯和“熱加載”是提升 Redux 開發者開發體驗的核心,也是 Redux Devtools 的功能所在。而且在 Redux 服務端渲染的場景中,可以把已經序列化的 action 儲存在服務器,然后在瀏覽器中重新序列化。
#### 更多信息
**文章**
- [可以將 store 的 state 設置為函數、promise 或者其它非序列化值嗎?](https://cn.redux.js.org/docs/faq/OrganizingState.html#organizing-state-non-serializable)
**討論**
- [#1171: Why doesn't Redux use classes for actions and reducers?](https://github.com/reactjs/redux/issues/1171#issuecomment-196819727)
<a id="why-currying"></a>
### 為什么 middleware 簽名是柯里化(currying)的形式?
有些人認為使用 [柯里化函數簽名] 的方式聲明 middleware 是 [沒有必要的](https://github.com/reactjs/redux/pull/784) ,因為當 applyMiddleware 函數執行時 store 和 next 都是存在的。這個 issue 已經被認為是 [不值得為此引入破壞性修改](https://github.com/reactjs/redux/issues/1744)。
#### 更多信息
**討論**
- 為什么 middleware 簽名是柯里化(currying)的形式?
- 參見 [#55](https://github.com/reactjs/redux/pull/55), [#534](https://github.com/reactjs/redux/issues/534), [#784](https://github.com/reactjs/redux/pull/784), [#922](https://github.com/reactjs/redux/issues/922), [#1744](https://github.com/reactjs/redux/issues/1744)
<a id="closure-dispatch"></a>
### 為什么 `applyMiddleware` 要為 `dispatch` 創建一個閉包?
`applyMiddleware` 從 store 中獲取已有的 dispatch,然后把它封裝在一個閉包中來創建最開始的 middleware 鏈。然后用一個對象調用來調用,以暴露出 getState 和 dispatch 函數。這樣做可以使得 middleware [在初始化時可以使用 dispatch](https://github.com/reactjs/redux/pull/1592)。
#### 更多信息
**討論**
- 為什么 `applyMiddleware` 要為 `dispatch` 創建一個閉包?
- 參見 [#1592](https://github.com/reactjs/redux/pull/1592) 以及 [#2097](https://github.com/reactjs/redux/issues/2097)
<a id="combineReducers-limitations"></a>
### 在 `combineReducers` 調用其所有 reducer 時,為什么不引入第三個參數來表示整個 state?
`combienReducer` 鼓勵你按照“域”(domain)來劃分 reducer 邏輯。正如 [`combineReducers` 進階](https://cn.redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html) 所說,`combineReducers` 被有意限制于單一的應用場景:把不同片段的 state 的更新工作委托給一個特定的 reducer,以此更新由普通的 JavaScript 對象構成的 state 樹。
對于每一個 reducer 來說,潛在的第三個參數應該是什么并不那么確定:可以是整個 state 樹、可以是某些回調函數、可以是 state 樹的其它部分、等等。如果 `combineReducers` 不符合你的使用場景,可以考慮使用其它的庫,比如 [combineSectionReducers](https://github.com/ryo33/combine-section-reducers) 或 [reduceReducers](https://github.com/acdlite/reduce-reducers),來獲取更多選項:諸如深層嵌套的 reducer 或能夠獲取到全局 state 的 reducer。
如果這些發布的工具都解決不了你的使用場景,你總是可以自行實現一個函數來精確地實現你的需求。
#### 更多信息
**文章**
- [`combineReducers` 進階](https://cn.redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html)
**討論**
- [#1768 Allow reducers to consult global state](https://github.com/reactjs/redux/pull/1768)
<a id="no-asynch-in-mapDispatchToProps"></a>
### 為什么 `mapDispatchToProps` 中不允許使用 `getState()` 或 `mapStateToProps()` 的返回值?
曾經有人希望在 `mapDispatch` 中使用整個 `state` 或 `mapState` 的返回值,這樣一來 `mapDispatch` 中聲明的函數就能拿到 `store` 中最新的返回值了。
然而 `mapDispatch` 并不支持這種做法,因為這么做意味著每次 store 更新后 `mapDispatch` 也需要被調用,導致每次 state 更新后函數需要重新創建,從而帶來很多性能開銷。
像這種“需要基于當前 state 以及 mapDispatchToProps 函數來替換 prop”的場景,正確的處理方式是使用 mergeProps(connect 函數的第三個參數)。如果這個參數被定義,它會被傳入 `mapStateToProps()` 和 `mapDispatchToProps()` 的結果、以及容器組件的 prop。而 `mergeProps` 返回的普通對象(plain object)會作為 prop 傳入包裝的組件(wrapped component)。
#### 更多信息
**討論**
- [#237 Why doesn't mapDispatchToProps allow use of return values from getState() or mapStateToProps()?](https://github.com/reactjs/react-redux/issues/237)
- 自述
- 介紹
- 動機
- 核心概念
- 三大原則
- 先前技術
- 學習資源
- 生態系統
- 示例
- 基礎
- Action
- Reducer
- Store
- 數據流
- 搭配 React
- 示例:Todo List
- 高級
- 異步 Action
- 異步數據流
- Middleware
- 搭配 React Router
- 示例:Reddit API
- 下一步
- 技巧
- 配置 Store
- 遷移到 Redux
- 使用對象展開運算符
- 減少樣板代碼
- 服務端渲染
- 編寫測試
- 計算衍生數據
- 實現撤銷重做
- 子應用隔離
- 組織 Reducer
- Reducer 基礎概念
- Reducer 基礎結構
- Reducer 邏輯拆分
- Reducer 重構示例
- combineReducers 用法
- combineReducers 進階
- State 范式化
- 管理范式化數據
- Reducer 邏輯復用
- 不可變更新模式
- 初始化 State
- 結合 Immutable.JS 使用 Redux
- 常見問題
- 綜合
- Reducer
- 組織 State
- 創建 Store
- Action
- 不可變數據
- 代碼結構
- 性能
- 設計哲學
- React Redux
- 其它
- 排錯
- 詞匯表
- API 文檔
- createStore
- Store
- combineReducers
- applyMiddleware
- bindActionCreators
- compose
- react-redux 文檔
- API
- 排錯