Redux源码分析之combineReducers
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的更多相关文章
- Redux源码分析之createStore
接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...
- Redux源码分析之applyMiddleware
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Redux源码分析之基本概念
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Redux源码分析之bindActionCreators
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- Redux源码分析之compose
Redux源码分析之基本概念 Redux源码分析之createStore Redux源码分析之bindActionCreators Redux源码分析之combineReducers Redux源码分 ...
- 史上最全的 Redux 源码分析
前言 用 React + Redux 已经一段时间了,记得刚开始用Redux 的时候感觉非常绕,总搞不起里面的关系,如果大家用一段时间Redux又看了它的源码话,对你的理解会有很大的帮助.看完后,在回 ...
- 正式学习React(四) ----Redux源码分析
今天看了下Redux的源码,竟然出奇的简单,好吧.简单翻译做下笔记: 喜欢的同学自己可以去github上看:点这里 createStore.js import isPlainObject from ' ...
- redux源码学习笔记 - combineReducers
上一篇有了解到,reducer函数的两个为:当前state和此次dispatch的action. state的结构是JavaScript对象,每个key都可以代表着不同意义的数据.比如说 { list ...
- Redux源码学习笔记
https://github.com/reduxjs/redux 版本 4.0.0 先了解一下redux是怎么用的,此处摘抄自阮一峰老师的<Redux 入门教程> // Web 应用是一个 ...
随机推荐
- fedora安装QQ
只看重利益的TC根本没想到要维护和更新linux版本的QQ,所幸fedora linux的中文社区 (https://repo.fdzh.org) 对大家比较照顾,还是针对fedora做了wine Q ...
- eclipse 和 jdk的位数不同
如果eclipse 和 jdk的位数不同,如一个64Bit一个32位,不需要修改环境变量,只需要在eclipse根目录的eclipse.ini文件中第一行加入:-vmD:\Program Files\ ...
- JSON总结-持续更新补充
基本的json格式 { "name": "jobs", "boolean": true, "age": null, &q ...
- USACO The Castle
首先看一下题目. The CastleIOI'94 - Day 1 In a stroke of luck almost beyond imagination, Farmer John was sen ...
- container_of 的用法
1.问题:如何通过结构中的某个变量获取结构本身的指针???关于container_of见kernel.h中:/*** container_of - cast a member of a structu ...
- Mongodb 参数说明及常见错误处理
在 CentOS7 上安装 MongoDB 1 通过 SecureCRT 连接至 CentOS7 服务器: 2 进入到 /usr/local/ 目录:cd /usr/local 3 在当前目录下 ...
- MyEclipse2014web工程项目直接复制不能访问报错处理方案
在学习web servlet项目中,做一个项目时 , 完成到了某一阶段 实现了部分功能,有必要保存这一项目,当复制这个项目之后发现发布会报错或者不能访问. 其实复制项目主要是为了在以后的学习中如果能顺 ...
- Egret的项目结构
这是我新建的一个Egret EUI项目 .wing文件夹是项目的配置文件 bin-debug 文件夹,项目编译和运行的debug目录 libs 文件夹,存放库文件,包括 Egret 核心库,其他扩展库 ...
- Struts2的知识点小总结
strust2是 struts1和webwork的结合 (本质相当于servlet) 核心:拦截器 interceptor action ognl和valuestack 使用struts的准备工作 ...
- Watson Product
This article will discuss Watson related products or services. I will add more detailed information ...