1. 程式人生 > >redux學習筆記之

redux學習筆記之

import { ActionTypes } from './createStore'
import isPlainObject from 'lodash/isPlainObject'
import warning from './utils/warning'

function getUndefinedStateErrorMessage(key, action) {
  var actionType = action && action.type
  var actionName = actionType && `"${actionType.toString()}"` || 'an action'

  return (
    `Given action ${actionName}, reducer "${key}" returned undefined. ` +
    `To ignore an action, you must explicitly return the previous state.`
  )
}

function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
  var reducerKeys = Object.keys(reducers)
  var argumentName = action && action.type === ActionTypes.INIT ?
    'initialState argument passed to createStore' :
    'previous state received by the reducer'

  if (reducerKeys.length === 0) {
    return (
      'Store does not have a valid reducer. Make sure the argument passed ' +
      'to combineReducers is an object whose values are reducers.'
    )
  }

  if (!isPlainObject(inputState)) {
    return (
      `The ${argumentName} has unexpected type of "` +
      ({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
      `". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join('", "')}"`
    )
  }

  var unexpectedKeys = Object.keys(inputState).filter(key => !reducers.hasOwnProperty(key))

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
      `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
    )
  }
}

function assertReducerSanity(reducers) {
  Object.keys(reducers).forEach(key => {
    var reducer = reducers[key]
    var initialState = reducer(undefined, { type: ActionTypes.INIT })

    if (typeof initialState === 'undefined') {
      throw new Error(
        `Reducer "${key}" returned undefined during initialization. ` +
        `If the state passed to the reducer is undefined, you must ` +
        `explicitly return the initial state. The initial state may ` +
        `not be undefined.`
      )
    }

    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')
    if (typeof reducer(undefined, { type }) === 'undefined') {
      throw new Error(
        `Reducer "${key}" returned undefined when probed with a random type. ` +
        `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +
        `namespace. They are considered private. Instead, you must return the ` +
        `current state for any unknown actions, unless it is undefined, ` +
        `in which case you must return the initial state, regardless of the ` +
        `action type. The initial state may not be undefined.`
      )
    }
  })
}

/**
 * Turns an object whose values are different reducer functions, into a single
 * reducer function. It will call every child reducer, and gather their results
 * into a single state object, whose keys correspond to the keys of the passed
 * reducer functions.
 *
 * @param {Object} reducers An object whose values correspond to different
 * reducer functions that need to be combined into one. One handy way to obtain
 * it is to use ES6 `import * as reducers` syntax. The reducers may never return
 * undefined for any action. Instead, they should return their initial state
 * if the state passed to them was undefined, and the current state for any
 * unrecognized action.
 *
 * @returns {Function} A reducer function that invokes every reducer inside the
 * passed object, and builds a state object with the same shape.
 */
export default function combineReducers(reducers) {
  var reducerKeys = Object.keys(reducers)
  var finalReducers = {}
  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i]
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  var finalReducerKeys = Object.keys(finalReducers)

  var sanityError
  try {
    assertReducerSanity(finalReducers)
  } catch (e) {
    sanityError = e
  }

  return function combination(state = {}, action) {
    if (sanityError) {
      throw sanityError
    }

    if (process.env.NODE_ENV !== 'production') {
      var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action)
      if (warningMessage) {
        warning(warningMessage)
      }
    }

    var hasChanged = false
    var nextState = {}
    for (var i = 0; i < finalReducerKeys.length; i++) {
      var key = finalReducerKeys[i]
      var reducer = finalReducers[key]
      var previousStateForKey = state[key]
      var nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

一片段一片段的分析:

var reducerKeys = Object.keys(reducers)
  var finalReducers = {}
  for (var i = 0; i < reducerKeys.length; i++) {
    var key = reducerKeys[i]
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  var finalReducerKeys = Object.keys(finalReducers)

該片段是將reducers物件的所有key取出並獲取其對應的reducer
 var sanityError
  try {
    assertReducerSanity(finalReducers)
  } catch (e) {
    sanityError = e
  }

assertReducerSanity函式原始碼:

Object.keys(reducers).forEach(key => {
    var reducer = reducers[key]
    var initialState = reducer(undefined, { type: ActionTypes.INIT })

    if (typeof initialState === 'undefined') {
      throw new Error(
        `Reducer "${key}" returned undefined during initialization. ` +
        `If the state passed to the reducer is undefined, you must ` +
        `explicitly return the initial state. The initial state may ` +
        `not be undefined.`
      )
    }

    var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')
    if (typeof reducer(undefined, { type }) === 'undefined') {
      throw new Error(
        `Reducer "${key}" returned undefined when probed with a random type. ` +
        `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +
        `namespace. They are considered private. Instead, you must return the ` +
        `current state for any unknown actions, unless it is undefined, ` +
        `in which case you must return the initial state, regardless of the ` +
        `action type. The initial state may not be undefined.`
      )
    }
  })


該片段是對reducer做合法性檢驗,檢驗的形式是①reducer(undefined, { type: ActionTypes.INIT }),先檢驗initialState返回的是不是undefined;②reducer(undefined, { type }),檢驗隨機的一個type是否會返回undefined。

之後的片段是返回一個函式,該函式會逐個呼叫每一個reducer,並返回一個單獨的物件state,物件的形式是key與Reducers的key保持一致。這個返回的函式才是最終的reducer,因此dispatch的時候是呼叫了這個函式,這樣的話每次都會呼叫每一個小的reducer(可以自己斷點來驗證),驗證結果如圖所示:


可以看到每次dispatch的時候兩個reducer都會被呼叫到,其中ADD_TODO在todos reducer中有case匹配到,會根據傳進來的action得到新的state,而ADD_TODO在visibility reducer中沒有case匹配到,會到default語句中,然後返回原來的結果。這樣就又合成新的state了,這就是combineReducer的機理。

前面4句是combineReducers函式內部呼叫的,主要是呼叫assertReducerSanity產生的結果,後面兩句是createStore函式內部呼叫dispatch({type:ActionTypes.INIT })產生的結果

 for (var i = 0; i < finalReducerKeys.length; i++) {
      var key = finalReducerKeys[i]
      var reducer = finalReducers[key]
      var previousStateForKey = state[key]
      var nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        var errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}

這部分的程式碼的意思是每一個reducer(previousStateForKey, action)返回的值就是新的state[key],比如todoReducer返回的值就是state[todoReducer]的值,最終每一個reducer給state一個key,最後組成一個完整的state.

state的形式會是這樣的:

{
    todods:......
    visible:......
}
注意1:state只有一個,所以這個state就是createStore創建出來的state。react-redux的connect中mapStateToProps的state引數就是這個state。state的屬性有幾個是根據combineReducers({reducer1,reducer2})中的引數有幾個reducer決定的。

注意2:var reducer = finalReducers[key]   var previousStateForKey = state[key]   var nextStateForKey = reducer(previousStateForKey, action) 這三句話表明到具體某一個reducer執行時,傳進去的是state對應key的值。比如todos Reducer傳進去的是state.todos,visibility Reducer傳進去的是state.visibility


最後在createStore(combineReducer({reducer1,reducer2},initialState)的時候內部還會呼叫一個dispatch({ type: ActionTypes.INIT }),具體可參考createStore原始碼:https://github.com/reactjs/redux/blob/master/src/createStore.js

附此部分原始碼:

// When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer
  }


相關推薦

redux學習筆記

import { ActionTypes } from './createStore' import isPlainObject from 'lodash/isPlainObject' import warning from './utils/warning' funct

SAS學習筆記函數應用

不能 oracle 理解 資料 oracl 函數應用 特殊 put acl 今天在做數據需求的時候遇到一些問題,因為不能夠在數據庫裏面做,僅僅好在SAS裏面實現。這就遇到了一些麻煩,須要使用一些函數實現部分功能,如查找字段中某個特殊字符出現的次數,查找某個字符的位置等,

c#學習筆記Application.DoEvents應用

box nbsp net replace 運算 技術 oop blank 假死 Visual Studio裏的摘要:處理當前在消息隊列中的所有 Windows 消息。 交出CPU控制權,讓系統可以處理隊列中的所有Windows消息 比如在大運算量循環內,加Applicati

[C#學習筆記異步編程模式2]BeginInvoke和EndInvoke方法 (轉載)

cti otf 函數返回 編程模式 catch 數值 gin 單線程 blog 為什麽要進行異步回調?眾所周知,普通方法運行,是單線程的,如果中途有大型操作(如:讀取大文件,大批量操作數據庫,網絡傳輸等),都會導致方法阻塞,表現在界面上就是,程序卡或者死掉,界面元素不動了,

Redux學習筆記-基礎知識

事件處理 學習筆記 情況 分發 .org 新的 分數 class 特點 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px "Helvetica Neue"; color: #404040 } p.p2 { margin

.NET學習筆記ADO.NET

技術分享 reader sql語句 bsp 技術 area ada 執行sql 學習 據提供程序: Connection,用來連接數據庫 Command,用來執行SQL語句 DataReader只讀,只進的結果集,一條一條讀取數據 DataAdapter,一個封裝了上面3個

,NET學習筆記ADO.NET

nbsp png alt 學習 log logs ima http blog ,NET學習筆記之ADO.NET

Metasploit學習筆記——情報搜集

user 技術 得到 版本號 域名 listing 避免 ger max 1.情報搜集 1.1外圍信息搜索 1.1.1通過DNS和IP地址挖掘目標網絡信息 (1)whois域名註冊信息查詢(BT5、kali專有):[email protected]

鋒利的JQuery學習筆記JQuery

function 效率 back 一點 ie6 容易 服務器端 pre loading   今天終於看到了最令我興奮的一章:JQuery與Ajax的應用。AJAX即“Asynchronous JavaScript and XML”(異步JavaScript和XML),

java入門學習筆記1(類的定義,代碼的編譯執行)

spa hex nts 自動調用 [] alt vim 進制 技術 這篇文章講解Java代碼的基本執行過程 我們先拋開各種JAVA IDE,開發工具,只使用文本編輯器,以突出最本質的東西。 在Linux環境下,我們編輯一個文件: vim HelloWorld.java

python學習筆記列表與元組

長度 bsp 最大 一般來說 設置 概述 檢查 常用 而且 一、概述 python包含6種內建的序列,其中列表和元組是最常用的兩種類型。列表和元組的主要區別在於,列表可以修改,元組則不能修改 使用上,如果要根據要求來添加元素,應當使用列表;而由於要求序列不可修改時,此時

Scala 學習筆記函數(1)

操作 函數賦值 array 筆記 val 類型 code function cnblogs 1 class Student { 2 3 val mySayHello1: String => Unit = sayHello 4 val mySayHe

Scala 學習筆記集合(2)

元素 student cnblogs cti 使用 shanghai 列表 學習筆記 brush class StudentTT extends StudentT{ def sayBye(name: String, age: Int)(address: String)

MySQ學習筆記十 NULL值處理

con pop -a 能夠 第一個 ips ng- 索引 one 這是MySQL一大特殊之處。 概念上。NULL意味著“沒有值”或“未知值”,且它被看作有點與眾不同的值。為了測試NULL。你不能使用算術比較

Scala 學習筆記集合(7) Option

bject fin pre object c cti abc collect 結果 == object CollectionDemo8 { def main(args: Array[String]): Unit = { //Option集合的使用,可以用來安全

C++學習筆記泛型算法

vector ace sort clu 算法 clas uniq bits 有時 先貼個代碼 有時間的再補筆記 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 //模板類在接收

Scala 學習筆記集合(9) 集合常用操作匯總

lis cti ring 結果 ray 常用操作 light logs bject object CollectionDemo10 { def main(args: Array[String]): Unit = { var ls = List[Int](1,

vue2.0學習筆記webpack-simple模板中的路由簡單配置案例

nbsp 主頁 default code vue 兩個文件 new ebp 命名 以下是完成後的目錄結構 案例基礎是已經用 webpack+vue-cli 創建了一個名為 vue_router的工程 , 並安裝了vue-rout

vue2.0學習筆記自定義組件

2.0 sco ron 自定義組件 定義 temp use 使用 imp step one: 推薦結構 step two: Loading.vue <template> <h3>{{msg}}<

PyTorch學習筆記初識word_embedding

spa clas eve rom embed con nbsp from print 1 import torch 2 import torch.nn as nn 3 from torch.autograd import Variable 4 5 word2i