Redux源碼閱讀_3

combineReducers.ts

函數(shù)重載聲明

首先是對combineReducers函數(shù)的重載无牵,重載了三個函數(shù),主要區(qū)別是傳參reducers類型的不同厂抖。

// 重載函數(shù)
export default function combineReducers<S>(
  reducers: ReducersMapObject<S, any>
): Reducer<CombinedState<S>>
export default function combineReducers<S, A extends Action = AnyAction>(
  reducers: ReducersMapObject<S, A>
): Reducer<CombinedState<S>, A>
export default function combineReducers<M extends ReducersMapObject>(
  reducers: M
): Reducer<
  CombinedState<StateFromReducersMapObject<M>>,
  ActionFromReducersMapObject<M>
>
函數(shù)結(jié)構(gòu)

然后看看combineReducers函數(shù)的結(jié)構(gòu)茎毁。


combineReducers函數(shù)結(jié)構(gòu)

可以看到combineReducers是個高階函數(shù),返回值也是一個函數(shù),返回的函數(shù)接收state與action兩個變量七蜘。在官方手冊給的用法谭溉,combineReducer之后是直接可以作為參數(shù)傳入給createStore(具體分析見Redux源碼閱讀_1(上))來生成store的,但是源碼的返回值卻是個函數(shù)橡卤,這是怎么回事扮念?

帶著這個問題繼續(xù)看……

函數(shù)體內(nèi)聲明的變量定義了兩種特殊的類型,ReducersMapObjectStateFromReducersMapObject碧库,看看這兩種類型的定義柜与。

export type Reducer<S = any, A extends Action = AnyAction> = (
  state: S | undefined,
  action: A
) => S

export type ReducersMapObject<S = any, A extends Action = AnyAction> = {
  [K in keyof S]: Reducer<S[K], A>
}

export type StateFromReducersMapObject<M> = M extends ReducersMapObject
  ? { [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never }
  : never

可以看到:

  • Reducers是入?yún)閟tate與action,且返回一個state的函數(shù)(state類型為S)瓦呼。

  • ReducersMapObject是value值為Reducer類型的Object栓辜。(之前以為ReducerMapObject是key值為state恨豁,value值為Reducer類型的Map,看了發(fā)現(xiàn)命名不太對迁匠。。定義的key是[K in keyof S]秕脓,state 為 S 類型柒瓣,而我們定義的state又是Object,那state的key就是string吠架,那么ReducerMapObject的key也是string類型芙贫,所以他應(yīng)該是個Object,而不是Map)

  • StateFromReducersMapObject是value值為是state的Object(之前以為類型是key值為Reducer類型傍药,value為state的Map磺平,但是定義的M繼承自ReducersMapObject,那么 M 的 key 類型為 string拐辽,那么 P 類型就是 string拣挪,所以他的類型應(yīng)該是Object而不是Map)。

現(xiàn)在就可以回答上面的問題了俱诸,combineReducers返回一個接受state與action的函數(shù)菠劝,而redux里定義的reducer也是這樣的一個函數(shù),所以combineReducers的返回值類型其實就是一個Reducer……(被自己蠢到了睁搭,因為想看源碼就把ts快速過了一遍赶诊,類型這一塊剛看有點懵,看源碼的時候一直在糾結(jié)這個變量是啥類型园骆、那個變量是啥類型的問題舔痪。。)

函數(shù)實現(xiàn)

combineReducers函數(shù)內(nèi)部主要操作:

  • 將value類型為function的所有reducers復(fù)制到finalReducers
  • 返回combine函數(shù)

combine函數(shù)的操作:

  • 新建 nextState : StateFromReducersMapObject<typeof reducers> 變量
  • 遍歷finalReducers锌唾,計算當(dāng)前state與nextState值锄码,并將nextState賦值給對應(yīng)key值的nextState,修改hasChanged標(biāo)識變量的值
  • 根據(jù)hasChanged標(biāo)識變量確定是返回傳入?yún)?shù)state還是新建變量nextState

由combine函數(shù)操作可知,redux返回的state都是全新的state滋捶,而不是在之前的state上做修改痛悯,其內(nèi)存地址有變化

具體代碼如下:

export default function combineReducers(reducers: ReducersMapObject) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers: ReducersMapObject = {}
  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}"`)
      }
    }

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)

  // This is used to make sure we don't warn about the same
  // keys multiple times.
  let unexpectedKeyCache: { [key: string]: true }
  if (process.env.NODE_ENV !== 'production') {
    unexpectedKeyCache = {}
  }

  let shapeAssertionError: Error
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  return function combination(
    state: StateFromReducersMapObject<typeof reducers> = {},
    action: AnyAction
  ) {
    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: StateFromReducersMapObject<typeof reducers> = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      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
    }
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市炬太,隨后出現(xiàn)的幾起案子灸蟆,更是在濱河造成了極大的恐慌,老刑警劉巖亲族,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炒考,死亡現(xiàn)場離奇詭異,居然都是意外死亡霎迫,警方通過查閱死者的電腦和手機斋枢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來知给,“玉大人瓤帚,你說我怎么就攤上這事∩” “怎么了戈次?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長筒扒。 經(jīng)常有香客問我怯邪,道長,這世上最難降的妖魔是什么花墩? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任悬秉,我火速辦了婚禮,結(jié)果婚禮上冰蘑,老公的妹妹穿的比我還像新娘和泌。我一直安慰自己,他們只是感情好祠肥,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布武氓。 她就那樣靜靜地躺著,像睡著了一般仇箱。 火紅的嫁衣襯著肌膚如雪县恕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天工碾,我揣著相機與錄音,去河邊找鬼百姓。 笑死渊额,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旬迹,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼火惊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奔垦?” 一聲冷哼從身側(cè)響起屹耐,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎椿猎,沒想到半個月后惶岭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡犯眠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年按灶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筐咧。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸯旁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出量蕊,到底是詐尸還是另有隱情铺罢,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布残炮,位于F島的核電站韭赘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吉殃。R本人自食惡果不足惜辞居,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛋勺。 院中可真熱鬧瓦灶,春花似錦、人聲如沸抱完。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巧娱。三九已至碉怔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間禁添,已是汗流浹背撮胧。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留老翘,地道東北人芹啥。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓锻离,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墓怀。 傳聞我的和親對象是個殘疾皇子汽纠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345