之前,已经写过一篇redux源码解读(一),主要分析了 redux 的核心思想,并用100多行代码实现一个简单的 redux 。但是,那个实现还不具备合并 reducer 和添加 middleware 的功能。

今天我们一起来看看合并 reducer (即 combineReducers) 的原理和实现。

在分析原理之前,先来看看combineReducers 的用法:

import { createStore, combineReducers } from 'redux';
const addScore = (state, action) => {};
const deleteScore = (state, action) => {};
const rootReducer = combineReducers({addStore, deleteScore});
const store = createStore(rootReducer);

从上面的例子可以看出,combineReducers 接收的参数类型是一个原生对象,其中这个对象的每个键值都是一个 reducer 纯函数。另外,因为 combineReducer() 返回的结果可以传递给createStore作为参数,可以推出它返回的结果也是一个 reducer 函数。

了解了 combineReducer 用法之后 ,那开始一步一步的用代码来实现其功能吧。

首先,需要先声明 combineReducers 的参数,然后判断传进来的参数是不是原生对象类型( plain object ),如果不是,则抛出异常,如果是,则需要获取该对象的所有属性(key)并存放到变量 reducerKeys 里面,然后对这些key进行遍历,过滤掉那些不是函数的值,并将结果放到 finalReducerKeys 里面。代码如下:

export default function combineReducers(reducers) {
// 判断参数reducers是否为对象
if(Object.prototype.toString.call(reducer) !== '[object Object]') {
throw new Error('combineReducers expected plain object params');
} const reducerKeys = Object.keys(reducers);
let finalReducerKeys = [];
// 过滤掉value不是Function类型的键名,然后将结果放到fianlReducerKeys里面
reducerKeys.forEach((key, i) => {
if(typeof reducers[key] === 'function') {
finalReducerKeys.push(key);
}
});
}

前面已经提到过了 combineReducers 返回的结果是一个纯函数。那这个返回的函数需要处理些什么逻辑呢?因为他合并了其他的 reducers,所以需要遍历这些 reducer 并执行他们。然后,并对比一下执行 reducer 之后的数据有没有变化 ,如果有变化则返回新的 state , 否则直接返回之前的 state。代码如下:

export default function combineReducers(reducers) {
// 省略和前面相同的部分 // 返回一个新的、经过组合的reducer函数
return function(state = {}, action) {
let hasChanged = false;
const nextState = {};
// 遍历finalReducerKeys,并调用对应的reducer。
finalReducerKeys.forEach((key, i) => {
const stateForKey = state[key];
const nextStateForKey = reducers[key](stateForKey, action);
nextState[key] = nextStateForKey;
// 如果前后状态不一样,则hasChanged设为true
if(stateForKey !== nextStateForKey) {
hasChanged = true;
}
});
// 如果有变化,则返回新的state,否则返回旧的
return hasChanged ? nextState : state;
}
}

OK,《redux 源码解读(二)》就写到这里,今天周五啦,祝大家周末愉快哈!如果对 combineReducer还有不明白的地方,欢迎留言讨论哈。另外,可能有些地方我分析得不到位的,建议到我的github去下载代码自己再好好研究一下。重要的事情说三遍:代码在这里下载! 代码在这里下载! 代码在这里下载!

redux源码解读(二)的更多相关文章

  1. Redux 源码解读 —— 从源码开始学 Redux

    已经快一年没有碰过 React 全家桶了,最近换了个项目组要用到 React 技术栈,所以最近又复习了一下:捡起旧知识的同时又有了一些新的收获,在这里作文以记之. 在阅读文章之前,最好已经知道如何使用 ...

  2. jQuery.Callbacks 源码解读二

    一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...

  3. redux源码解读(一)

    redux 的源码虽然代码量并不多(除去注释大概300行吧).但是,因为函数式编程的思想在里面体现得淋漓尽致,理解起来并不太容易,所以准备使用三篇文章来分析. 第一篇,主要研究 redux 的核心思想 ...

  4. redux源码解读

    react在做大型项目的时候,前端的数据一般会越来越复杂,状态的变化难以跟踪.无法预测,而redux可以很好的结合react使用,保证数据的单向流动,可以很好的管理整个项目的状态,但是具体来说,下面是 ...

  5. (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...

  6. 手把手教你撸一套Redux(Redux源码解读)

    Redux 版本:3.7.2 Redux 是 JavaScript 状态容器,提供可预测化的状态管理. 说白了Redux就是一个数据存储工具,所以数据基础模型有get方法,set方法以及数据改变后通知 ...

  7. mybatis源码解读(二)——构建Configuration对象

    Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...

  8. ConcurrentHashMap源码解读二

    接下来就讲解put里面的三个方法,分别是 1.数组初始化方法initTable() 2.线程协助扩容方法helpTransfer() 3.计数方法addCount() 首先是数组初始化,再将源码之前, ...

  9. go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    nsqlookupd: 官方文档解释见:http://bitly.github.io/nsq/components/nsqlookupd.html 用官方话来讲是:nsqlookupd管理拓扑信息,客 ...

随机推荐

  1. Linux上的10个Touch命令实例

    Linux Touch 命令 Touch命令可以用来修改文件访问或修改的时间戳.实际上,它更常仅仅用于快速的创建一个空白文件. 这篇文章展示了一些非常简单和快速的实例,使用Touch命令来修改时间戳和 ...

  2. Docker install GitLab

    示范一下如何透过Docker安装GitLab,也顺便将一些常用的东西纪录一下 作业系统: CentOS 7 安装Docker CE 1. 先移除系统上预先安装的Docker旧版本 yum remove ...

  3. Mac下安装mongdb

    使用 homebrew 安装 MongoDB :brew install mongodb 这时 MongoDB 将被安装在 /usr/local/Cellar/mongodb/4.0.3_1 (我的 ...

  4. java 学习笔记

    charAt(1) ;返回下表为1 的 length 返回长度 indexOf("XXX")返回XX在的位置(开始位置) startWith() y以什么开始 endWith()以 ...

  5. 20175223 姚明宇 MyCP

    目录 MyCP 要求 代码运行编译及文本输出输入结果 目录树 代码运行编译: 文本输出输入结果: 源代码 码云链接 目录 MyCP 要求 编写MyCP.java 实现类似Linux下cp XXX1 X ...

  6. DFS和BFS

    BFS 代码步骤: 1.写出每个点和每个点的邻接点的对应关系 2.方法参数:传一个对应关系和起始点 3.创建一个队列,然后每次都移除第一个,然后把移除的邻接点添加进去,打印取出的第一个,然后循环,一直 ...

  7. Docker入门教程

    一.入门介绍 Docker是一个开源引擎,类似于一个集装箱,开发者通过它可以为任何应用创建一个轻量级.环境无关可移植的容器.开发者在本地编译测试过的容器可以在不同的环境中部署. 通常适用于如下场景: ...

  8. Flutter不完全安裝指南(AndroidStudio集成)

    Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面.真心無力吐槽這些所谓的中文站以及社区的文档,整理一下安装流程吧. 本人是android开发,基于此基础上 ...

  9. docker安装mysql容器后,是用navicat连接报client does not support authentication protocol requested by server consider upgrading mysql client

    #进入容器 docker exec -it mysql bash#进入mysqlmysql -u root -p#重置密码ALTER USER 'root'@'%' IDENTIFIED WITH m ...

  10. Java程序第二次作业

    1.编写“人”类及其测试类.1.1 “人”类: 类名:Person 属性:姓名.性别.年龄.身份证号码 方法:在控制台输出各个信息1.2 测试类 类名:TestPerson 方法:main ...