Redux 中的CombineReducer的函數詳解


combineReducers(reducers)

 

隨着應用變得復雜,需要對 reducer 函數 進行拆分,拆分后的每一塊獨立負責管理 state 的一部分。

combineReducers 輔助函數的作用是,把一個由多個不同 reducer 函數作為 value 的 object,合並成一個最終的 reducer 函數,然后就可以對這個 reducer 調用 createStore。

合並后的 reducer 可以調用各個子 reducer,並把它們的結果合並成一個 state 對象。state 對象的結構由傳入的多個 reducer 的 key 決定。

最終,state 對象的結構會是這樣的:

{
  reducer1: ... reducer2: ... }

通過為傳入對象的 reducer 命名不同來控制 state key 的命名。例如,你可以調用 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 將 state 結構變為 { todos, counter }。

通常的做法是命名 reducer,然后 state 再去分割那些信息,因此你可以使用 ES6 的簡寫方法:combineReducers({ counter, todos })。這與 combineReducers({ counter: counter, todos: todos }) 一樣。

Flux 用戶使用須知

本函數可以幫助你組織多個 reducer,使它們分別管理自身相關聯的 state。類似於 Flux 中的多個 store 分別管理不同的 state。在 Redux 中,只有一個 store,但是 combineReducers 讓你擁有多個 reducer,同時保持各自負責邏輯塊的獨立性。

參數

reducers (Object): 一個對象,它的值(value) 對應不同的 reducer 函數,這些 reducer 函數后面會被合並成一個。下面會介紹傳入 reducer 函數需要滿足的規則。

之前的文檔曾建議使用 ES6 的 import * as reducers 語法來獲得 reducer 對象。這一點造成了很多疑問,因此現在建議在 reducers/index.js 里使用 combineReducers() 來對外輸出一個 reducer。下面有示例說明。

返回值

(Function):一個調用 reducers 對象里所有 reducer 的 reducer,並且構造一個與 reducers 對象結構相同的 state 對象。

注意

本函數設計的時候有點偏主觀,就是為了避免新手犯一些常見錯誤。也因些我們故意設定一些規則,但如果你自己手動編寫根 redcuer 時並不需要遵守這些規則。

每個傳入 combineReducers 的 reducer 都需滿足以下規則:

  • 所有未匹配到的 action,必須把它接收到的第一個參數也就是那個 state 原封不動返回。

  • 永遠不能返回 undefined。當過早 return 時非常容易犯這個錯誤,為了避免錯誤擴散,遇到這種情況時 combineReducers 會拋異常。

  • 如果傳入的 state 就是 undefined,一定要返回對應 reducer 的初始 state。根據上一條規則,初始 state 禁止使用 undefined。使用 ES6 的默認參數值語法來設置初始 state 很容易,但你也可以手動檢查第一個參數是否為 undefined。

雖然 combineReducers 自動幫你檢查 reducer 是否符合以上規則,但你也應該牢記,並盡量遵守。

示例

reducers/todos.js

export default function todos(state = [], action) { switch (action.type) { case 'ADD_TODO': return state.concat([action.text]) default: return state } }

reducers/counter.js

export default function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: return state } }

reducers/index.js

import { combineReducers } from 'redux' import todos from './todos' import counter from './counter' export default combineReducers({ todos, counter })

App.js

import { createStore } from 'redux' import reducer from './reducers/index' let store = createStore(reducer) console.log(store.getState()) // { // counter: 0, // todos: [] // } store.dispatch({ type: 'ADD_TODO', text: 'Use Redux' }) console.log(store.getState()) // { // counter: 0, // todos: [ 'Use Redux' ] // } 

小貼士

  • 本方法只是起輔助作用!你可以自行實現不同功能的 combineReducers,甚至像實現其它函數一樣,明確地寫一個根 reducer 函數,用它把子 reducer 手動組裝成 state 對象。

  • 在 reducer 層級的任何一級都可以調用 combineReducers。並不是一定要在最外層。實際上,你可以把一些復雜的子 reducer 拆分成單獨的孫子級 reducer,甚至更多層。

(二)內部分析

看了第一部分官網的介紹,大家都應該知道了它是如何使用的,但是作為開發人員,我覺得我們有必要思考一下它里面的究竟是如何實現的呢?下面我們來分析一下。

第一步,我們知道reducer 就是一個函數,接收舊的 state 和 action,返回新的 state。知道這點很重要!

第二步,我們來看combineReducers(reducers)方法,把一個由多個不同 reducer 函數作為 value 的 object,合並成一個最終的 reducer 函數,然后就可以對這個 reducer 調用 createStore。

那么combineReducers(reducers)返回的就是一個最終的reducer,reducer里面會返回新的state。下面我們結合代碼來看:

const rootReducer = combineReducers({
  reducer1,
  reducer2,
  reducer3,
  ... });

這里根reducer暫且起名為rootReducer,它就是通過combineReducers(reducers)返回的。那么combineReducers(reducers)是如何做的呢?

第三步,我們自己來寫一個function,來代替combineReducers(reducers)方法,從而來摸索內部是如何實現的,新建一個方法,如下:

var combineReducers1 = function(obj){ //內部具體代碼 }

 

這個方法返回的肯定是一個reducer,那么我們先寫出來:

var combineReducers1 = function(obj){ //內部具體代碼 //返回最終的reducer return reducer; }

我們應該能想到下一步該做什么了,既然返回一個reducer,那么我們就要創建一個reducer了,方法接收兩個參數,一個是state,一個是action,繼續:

var combineReducers1 = function(obj){ //內部具體代碼 function reducer(state,action){ //reducer具體邏輯 } //返回最終的reducer return reducer; }

reducer最終返回的是一個state,我們接着寫:

var combineReducers1 = function(obj){ //內部具體代碼 var finalState = {}; function reducer(state,action){ //reducer具體邏輯 //返回state return finalState; } //返回最終的reducer return reducer; }

那么現在想一想,我們傳入的object對象,實際上就是我們傳入的所有的reducer方法的集合,實際上里面做的就是分別調用每個reducer方法,將每個reducer方法作為value值賦予我們傳入object對象的屬性名,通過JavaScript遍歷對象獲取屬性名賦值的方法,我們可以得到最關鍵的代碼,接着往下寫:

var combineReducers1 = function(obj){ //內部具體代碼 var finalState = {}; function reducer(state,action){ //reducer具體邏輯 for (var p in obj) { //根據key屬性值調用function(state.屬性名,action) finalState[p] = obj[p](state[p], action); } //返回state return finalState; } //返回最終的reducer return reducer; }

 

因為我們reducer()方法里傳入的state,其實是根state,所以得根據屬性名來獲取對應的reducer上的state,到這里,關於combineReducers()方法的具體實現我們已經分析完了,了解了combineReducers是如何工作的,那么我們以后的工作才更好的展開,不至於出現了問題而不知道如何解決。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM