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

最新文章

  1. 解决CURL 请求本地超时
  2. JS函数输出圆的半径和面积
  3. SSL使用windows证书库中证书实现双向认证
  4. 解决JSP 不解析EL表达式
  5. NOIP2014 无线网络发射器选址
  6. Android中使用proguardgui混淆jar包
  7. masonry结合json 制作无限滚动的瀑布流
  8. AMD模块化JS
  9. FileSystemWatcher类监控文件的更改状态并且实时备份文件
  10. Http协议&amp;Servlet
  11. python之字典的增删改查
  12. MySQL行转列与列转行
  13. 最长子串(FZU2128)
  14. centos6.5新增加硬盘挂载并实现开机自动挂载
  15. GEF最简单的入门-helloword(1)
  16. input/radio/select等标签的值获取和赋值
  17. bug 问题
  18. C#模拟请求,模拟登录,Cookie设置、文件上传等问题汇总
  19. 使用Caffe训练适合自己样本集的AlexNet网络模型,并对其进行分类
  20. php 生成唯一id的几种解决方法(实例)

热门文章

  1. Tomcat启动报错java.lang.UnsatisfiedLinkError
  2. HTML4,HTML5,XHTML 之间有什么区别?
  3. TCP连接中time_wait在开发中的影响-搜人以鱼不如授之以渔
  4. zepto的使用方法
  5. 通过java反射得到javabean的属性名称和值参考
  6. 一些爬虫中的snippet
  7. [leetcode-630-Course Schedule III]
  8. 【Android Developers Training】 13. 支持不同平台版本
  9. centos生成公钥私钥 securecrt通过公钥访问服务器 winscp通过公钥访问服务器
  10. app 选项卡代码