1. 程式人生 > >Redux原始碼分析(二)之combineReducers

Redux原始碼分析(二)之combineReducers

Redux原始碼分析(combineReducers)

上一篇我們看完了createStore這個自認為最為核心的檔案之後,我們再來看下combineReducers.js這個檔案,其他它最主要的作用就是合併多個reducer,因為在createStore中的第一個引數就是一個reducer,而平時我們開發過程中如果將全部狀態都寫在一個檔案中有一些過於龐大且不好管理,因此我們可以先拆開去寫reducer,之後再用combineReducers合併就ok了,下面我們來看程式碼

import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'

function getUndefinedStateErrorMessage(key, action)
function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
)
// 這個方法用於檢測用於組合的reducer是否是符合redux規定的reducer
function assertReducerShape(reducers)

前面三個函式主要是針對一些警告處理的提示資訊函式,另外import了三個檔案,第一個ActionTypes只是初始化時定義的action名稱沒什麼了,以後我們也可以按照這種方式去存actionTypeName,第二個也是一個警告函數了,第三個是判斷物件是否為plain object,說白一點就是最簡單的物件不存在繼承,沒有proto。下面的combineReducers也就是該檔案的核心

export default function combineReducers(reducers) {
    // 遍歷全部的reducers,得到一個key陣列
    const reducerKeys = Object.keys(reducers)
    const finalReducers = {}
    for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
        // 判空警告
        if (process.env.NODE_ENV !== 'production') {
            if (typeof reducers[key] === 'undefined') {
                warning(`No reducer provided for key "${key}"`)
            }
        }

        // 將reducers全部內容拷貝到finalReducers中,日後對該物件進行操作
        if (typeof reducers[key] === 'function') {
            finalReducers[key] = reducers[key]
        }
    }
    // 遍歷finalReducers的keys
    const finalReducerKeys = Object.keys(finalReducers)

    // 意外key的快取物件
    let unexpectedKeyCache
    if (process.env.NODE_ENV !== 'production') {
        unexpectedKeyCache = {}
    }

    // 斷言error
    let shapeAssertionError
    try {
        assertReducerShape(finalReducers)
    } catch (e) {
        shapeAssertionError = e
    }

    // 這些內容才是關鍵合併的地方,返回的就是一個函式,而這個函式就是一個reducer,所以引數也是state和action
    return function combination(state = {}, action) {
        // 依然是異常處理
        if (shapeAssertionError) {
            throw shapeAssertionError
        }

        // 異常處理警告
        if (process.env.NODE_ENV !== 'production') {
            const warningMessage = getUnexpectedStateShapeWarningMessage(
                state,
                finalReducers,
                action,
                unexpectedKeyCache
            )
            if (warningMessage) {
                warning(warningMessage)
            }
        }

        let hasChanged = false
        const nextState = {}
        // 遍歷全部的reducer
        for (let i = 0; i < finalReducerKeys.length; i++) {
            const key = finalReducerKeys[i]
            const reducer = finalReducers[key]
            const previousStateForKey = state[key]
            // 執行reducer,得到每個reducer計算後的狀態物件
            const nextStateForKey = reducer(previousStateForKey, action)
            if (typeof nextStateForKey === 'undefined') {
                const errorMessage = getUndefinedStateErrorMessage(key, action)
                throw new Error(errorMessage)
            }
            // 把全部的狀態物件組合成一個 新的狀態物件
            nextState[key] = nextStateForKey
            hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
        // 判斷是否變化,有變化則返回最新的state
        return hasChanged ? nextState : state
    }
}

其實總結起來也很簡單,就是遍歷全部的reducer,通過執行每個reducer得到狀態物件,最後在將各個得到的計算值組合成一個大的狀態物件,因為reducer本身的作用通過action得到變化的最新的狀態物件。