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. 移动端APP CSS Reset及注意事项CSS重置

    @charset "utf-8"; * { -webkit-box-sizing: border-box; box-sizing: border-box; } //禁止文本缩放 h ...

  2. 关于对WEB标准以及W3C的理解与认识问题

    web标准简单来说可以分为结构.表现和行为.其中结构主要是有HTML标签组成.或许通俗点说,在页面body里面我们写入的标签都是为了页面的结构.表现即指css样式表,通过css可以是页面的结构标签更具 ...

  3. mongodb入门笔记

    mongodb作为nosql中排名第一的数据库,近年来使用的人数越来越多,作为开发人员,非常有必要了解下mongodb数据库.下面就给大家介绍下mongodb数据库的基本知识,有不对的地方欢迎指正,Q ...

  4. springboot1.5.4 配置druid1.1.0(使用druid-spring-boot-starter)

    原文:https://github.com/x113773/testall/issues/11 ### Druid 最近发布了1.1.0 版本,并且提供了 [druid-spring-boot-sta ...

  5. 学编程担心自己英语不好吗?(IT软件开发常用英语词汇)

    发一份,我们导师的收集的常用词汇,与大家共享 欢迎加入Java学习交流裙六一六九五九四四四! S 欢迎加入Java学习交流裙 六一六  九五九  四四四!

  6. 点击率模型AUC

    一 背景       首先举个例子:                          正样本(90)                       负样本(10)         模型1预测      ...

  7. python yield from 语法

    python yield from 语法 yield语法比较简单, 教程也很多 , yield from的中文讲解很少 , python官网是这样解释的 PEP 380 adds the yield ...

  8. 在Visual Studio 2017中使用Asp.Net Core构建Angular4应用程序

    前言 Visual Studio 2017已经发布了很久了.做为集成了Asp.Net Core 1.1的地表最强IDE工具,越来越受.NET系的开发人员追捧. 随着Google Angular4的发布 ...

  9. thinkphp的空控制器和空操作以及对应解决方法

    在上篇随笔中我们已经知道了tp框架的四种访问方式,那么当在地址栏输入不存在的操作方法.控制器会怎么样呢? 先看一下定义: 空操作:一个对象(控制器)调用本身不存在的方法 空控制器:在实例化控制器对象的 ...

  10. CSS自定义动画

     动画名称  animation-name   :   (动画名称必须与@keyfarmes的名称相对应,因为动画名称是由@keyfarmes定义的) 例如:animation-name:AA:则 @ ...