Redux源码分析之基本概念

Redux源码分析之createStore

Redux源码分析之bindActionCreators

Redux源码分析之combineReducers

Redux源码分析之compose

Redux源码分析之applyMiddleware

combineReducers:把recuder函数们,合并成一个新的reducer函数,dispatch的时候,挨个执行每个reducer

我们依旧先看一下combineReduers的使用效果

let { createStore, bindActionCreators, combineReducers } = self.Redux

//默认state
let todoList = [], couter = 0
// reducer
let todoReducer = function (state = todoList, action) {
switch (action.type) {
case 'add':
return [...state, action.todo]
case 'delete':
return state.filter(todo => todo.id !== action.id)
default:
return state
}
},
couterReducer = function (state = couter, action) {
switch (action.type) {
case 'add':
return ++state
case 'decrease':
return --state
default:
return state
}
} var reducer = combineReducers({ todoReducer, couterReducer }) //创建store
let store = createStore(reducer) //订阅
function subscribe1Fn() {
// 输出state
console.log(store.getState())
}
store.subscribe(subscribe1Fn) // action creater
let actionCreaters = {
add: function (todo) { //添加
return {
type: 'add',
todo
}
}, delete: function (id) {
return {
type: 'delete',
id
}
}
} let boundActions = bindActionCreators(actionCreaters, store.dispatch)
console.log('todo add')
boundActions.add({
id:
12,
content: '睡觉觉'

})
let boundAdd = bindActionCreators(actionCreaters.add, store.dispatch)
console.log('todo add')
boundAdd({
id:
13,
content: '陪媳妇'

})
let counterActionCreater = {
add: function () {
return {
type: 'add'
}
},
decrease: function () {
return {
type: 'decrease'
}
}
} let boundCouterActions = bindActionCreators(counterActionCreater, store.dispatch) console.log('counter add:')
boundCouterActions.add()
console.log('counter decrease:')
boundCouterActions.decrease()

下面是执行结果

   我们一起分析一下:

  • 执行todo add的时候,看到counterReducer和 todoReducer的数据都有更新,说明两个reducer都执行了。
  • 执行counter add的时候,同样两个recuder都执行,但是因为没有参数,加入的是无效数据,这里就提示我们,是不是该进行一些必要的参数判断呢
  • 执行counter decrease的时候,同样两个reducer都执行,但是 todoReducer没有tyepe为decrease的action处理函数,当然没有任何产出

我们再回归源码,删除一些判断的代码逻辑,简化后如下:

  • 过滤一下reducer,把reducer和key都存起来
  • 返回一个新的reducer函数,新的reducer函数执行的时候,便利存起来的reducer,挨个执行
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] =
reducers[key]
}

}
const finalReducerKeys = Object.keys(finalReducers) return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
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)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}

这里额外的分析一下,当store的recuder是复合型的时候,怎么初始化state的

createStore传入的第一个参数recuder,是调用 combineReducers 新生成的reducer(依旧是一个函数)

createStore方法返回之前,会这样一下dispatch({ type: ActionTypes.INIT }),disptach的里面我们就关心下面几句

  try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}

也就是执行一下新的reducer,我们继续切换到新的reducer代码里面,同样只关心下面的代码

  return function combination(state = {}, action) { 

    let hasChanged = false
const nextState = {}
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)
nextState[key] = nextStateForKey

hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}

我们就看我们这个例子 combineReducers({ todoReducer, couterReducer }), 那么上面的key就会是  todoReducer, couterReducer, 那么初始化完毕的state得数据结构就是这样的

{todoReducer:....,couterReducer:......},

有人会想,初始化state,你上次不是用了两种方式,我这里只能说对不起,当你用的是复合型的reducer初始化state的时候,你用第二个参数来初始化state行不通的,

因为为了方便解析代码,上面我是省略了一部分的 ,下面再看看更完整一点的代码(我还是省略了一下)

export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers) let shapeAssertionError
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
} return function combination(state = {}, action) {
if (shapeAssertionError) {
throw
shapeAssertionError
}

.......
}

这里如果你没通过 aessertRecucerShape检查,是没法进行下去的,我们那看看aessertRecucerShape是个啥玩意,看备注。

function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT }) // 传入 undefined,让recuder默认值生效, if (typeof initialState === 'undefined') { // 如果没有默认值,返回的state就是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. If you don't want to set a value for this reducer, ` +
`you can use null instead of undefined.`
)
} const type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')
if (typeof reducer(undefined, { type }) === 'undefined') { // 这个主要是防止在recuder你真的自己定义了对type为
ActionTypes.INIT处理,创建一个随机的type,测试一下,你应该返回的是有效的state
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, but can be null.` ) } }) }

这就说明了上述的问题,(-_-)

回顾

1.  combineReducers 的参数是一个对象

2. 执行结果返回的依旧是一个reducer

3. 通过combineReducers 返回的reducer创建的store, 再派发某个action的时候,实际上每个内在的reducer都会执行

4. createStrore使用合成的reducer创建的store, 他再派发action返回的是总的大的state

Redux源码分析之combineReducers的更多相关文章

  1. Redux源码分析之createStore

    接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...

  2. Redux源码分析之applyMiddleware

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  3. Redux源码分析之基本概念

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  4. Redux源码分析之bindActionCreators

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  5. Redux源码分析之compose

    Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...

  6. 史上最全的 Redux 源码分析

    前言 用 React + Redux 已经一段时间了,记得刚开始用Redux 的时候感觉非常绕,总搞不起里面的关系,如果大家用一段时间Redux又看了它的源码话,对你的理解会有很大的帮助.看完后,在回 ...

  7. 正式学习React(四) ----Redux源码分析

    今天看了下Redux的源码,竟然出奇的简单,好吧.简单翻译做下笔记: 喜欢的同学自己可以去github上看:点这里 createStore.js import isPlainObject from ' ...

  8. redux源码学习笔记 - combineReducers

    上一篇有了解到,reducer函数的两个为:当前state和此次dispatch的action. state的结构是JavaScript对象,每个key都可以代表着不同意义的数据.比如说 { list ...

  9. Redux源码学习笔记

    https://github.com/reduxjs/redux 版本 4.0.0 先了解一下redux是怎么用的,此处摘抄自阮一峰老师的<Redux 入门教程> // Web 应用是一个 ...

随机推荐

  1. 原生javascript实现网页显示日期时钟效果

    刚接触javascript中Date内置对象时,以为这些方法都太简单了,结果要自己实际操作写一个时钟效果还真一时把我难住了,主要有几点大家要注意的.先看实际效果 要实现这样的效果 某年某月某日星期几几 ...

  2. centos 6.6 ios镜像文件 下载 官网和阿里云两种方式教你下载

    1百度一下:centos 打开打开官网.选择这一项 CET CENTOS 2选择 DVD ISO,双击下载 直接选择左键点击下载 这里需要迅雷 方法 二 打开 https://mirrors.aliy ...

  3. 2017-06-22初识python

    初识python #!/usr/bin/env python (python解释器的文件路径)# -*- coding:utf-8 -*- (使用的编码内型)# python 2.7 <需要加第 ...

  4. 搭建带热更新功能的本地开发node server

    引言 使用webpack有一段时间了,对其中的热更新的大概理解是:对某个模块做了修改,页面只做局部更新而不需要刷新整个页面来进行更新.这样就能节省因为整个页面刷新所产生开销的时间,模块热加载加快了开发 ...

  5. jquery获得表格可见行的大小数量

    alert($("#tableId").find("tbody tr[moban='true']").find(":visible").si ...

  6. Spring的JDBC(非web程序)的简单例子

    第一步: spring配置applicationContext.xml文件,放在src下面: <?xml version="1.0" encoding="UTF-8 ...

  7. CNN中的卷积核及TensorFlow中卷积的各种实现

    声明: 1. 我和每一个应该看这篇博文的人一样,都是初学者,都是小菜鸟,我发布博文只是希望加深学习印象并与大家讨论. 2. 我不确定的地方用了"应该"二字 首先,通俗说一下,CNN ...

  8. 彩扩机项目--NPN和PNP三极管作为开关管的区别

    上图是最终画好的电路.使用的是NPN三极管,并且把NPN三极管放在了下面.下面分析下NPN三极管作为开关管能否放在上面. 从上面两张图分析可知,当三极管作为开关管使用的时候,NPN三极管需要放在下面( ...

  9. 巧用五招提升Discuz!X运行速度

    Discuz!X使用的是数据库应用程序,所以,当数据库的大小.帖子的数目.会员的数目,这些因素都会影响到程序的检索速度,尤其是当论坛的影响力大了,这个问题就更为突出了,虽然,康盛对Discuz进行了更 ...

  10. pouchdb-find( pouchdb查询扩展插件 ,便于查询)

    pouchdb-find pouchdb-find 环境搭建 下载lib bower install pouchdb-find 引入js <script src="pouchdb.js ...