combineReducers

combineReducer 是将众多的 reducer 合成通过键值映射的对象,并且返回一个 combination 函数传入到 createStore 中

合并后的 combination 能够调用个子 reducer,并且对 state 状态进行更新

源码:

import { ActionTypes } from "./createStore";
import isPlainObject from "lodash/isPlainObject";
import warning from "./utils/warning"; //总体上就是根据key和action生成xxx在xxx中出现错误,你需要xxx的错误信息
function getUndefinedStateErrorMessage(key, action) {
const actionType = action && action.type;
const actionDescription = (actionType && `action "${String(actionType)}"`) || "an action"; return `Given ${actionDescription}, reducer "${key}" returned undefined. ` + `To ignore an action, you must explicitly return the previous state. ` + `If you want this reducer to hold no value, you can return null instead of undefined.`;
} function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
const reducerKeys = Object.keys(reducers);
const argumentName = action && action.type === ActionTypes.INIT ? "preloadedState argument passed to createStore" : "previous state received by the reducer"; //reducer是一个空对象,没有键值
if (reducerKeys.length === 0) {
return "Store does not have a valid reducer. Make sure the argument passed " + "to combineReducers is an object whose values are reducers.";
} //检查 value 是否是普通对象。 也就是说该对象由 Object 构造函数创建,或者 [[Prototype]] 为 null
//https://www.html.cn/doc/lodash/#_isplainobjectvalue
if (!isPlainObject(inputState)) {
return `The ${argumentName} has unexpected type of "` + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + `". Expected argument to be an object with the following ` + `keys: "${reducerKeys.join('", "')}"`;
} //如果一些key在state中存在,而在reducer中不存在,则添加至unexpectedKeyCache中并输出警告信息
const unexpectedKeys = Object.keys(inputState).filter(key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]); unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true;
}); if (unexpectedKeys.length > 0) {
return `Unexpected ${unexpectedKeys.length > 1 ? "keys" : "key"} ` + `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` + `Expected to find one of the known reducer keys instead: ` + `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`;
}
} //判断reducer是否符合redux的标准
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key];
// 通过ActionTypes.INIT来(oldState,action) => return newState测试reducer是否正确
const initialState = reducer(undefined, { type: ActionTypes.INIT }); //如果返回是undefined说明reducer内部出错,不符合使用标准
if (typeof initialState === "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("."); // 通过随机生成type来(oldState,action) => return newState测试reducer是否正确,如果返回是undefined说明reducer内部出错,不符合使用标准
if (typeof reducer(undefined, { type }) === "undefined") {
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.`);
}
});
} /**
* Turns an object whose values are different reducer functions, into a single
* reducer function. It will call every child reducer, and gather their results
* into a single state object, whose keys correspond to the keys of the passed
* reducer functions.
*
* @param {Object} reducers An object whose values correspond to different
* reducer functions that need to be combined into one. One handy way to obtain
* it is to use ES6 `import * as reducers` syntax. The reducers may never return
* undefined for any action. Instead, they should return their initial state
* if the state passed to them was undefined, and the current state for any
* unrecognized action.
*
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/ //这个函数可以组合一组 reducers(对象) ,然后返回一个新的 reducer 函数给 createStore 使用
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
//用来存放过滤后的值
const finalReducers = {};
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]; //开发环境下如果reducer是undefined,报警
if (process.env.NODE_ENV !== "production") {
if (typeof reducers[key] === "undefined") {
warning(`No reducer provided for key "${key}"`);
}
} //如果reducer不是一个函数,就过滤掉
if (typeof reducers[key] === "function") {
finalReducers[key] = reducers[key];
}
}
const finalReducerKeys = Object.keys(finalReducers); let unexpectedKeyCache;
if (process.env.NODE_ENV !== "production") {
unexpectedKeyCache = {};
} //第二次过滤,将finalReducer中不符合redux的标准的reducer去掉
let shapeAssertionError;
try {
assertReducerShape(finalReducers);
} catch (e) {
shapeAssertionError = e;
} //整个combineReducer就是返回一个 combination 函数,该函数将传入createStore 中
return function combination(state = {}, action) {
//当执行第二次过滤是出现错误,抛出
if (shapeAssertionError) {
throw shapeAssertionError;
} if (process.env.NODE_ENV !== "production") {
//开发环境下,报警
const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
if (warningMessage) {
warning(warningMessage);
}
} let hasChanged = false;
const nextState = {};
//遍历所有的reducer来执行(oldState,action)=>newState,根据hanChanged来判断是返回新state还是旧state (性能)
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]; //当前key值
const reducer = finalReducers[key]; //当前key值对应的函数
const previousStateForKey = state[key]; //当前reducer的旧状态
const nextStateForKey = reducer(previousStateForKey, action); //为reducer计算出新state
if (typeof nextStateForKey === "undefined") {
// 如果计算出的新state是undefiend,通过getUndefinedStateErrorMessage拼接个报错信息,抛出去
const errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
//将所有新计算的state组成新state树
nextState[key] = nextStateForKey;
//判断新state是否等于旧state,如果不同就将hasChanged设为false,代表整个状态都改变了(只要有一个不同)
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
// 根据hasChanged的值来返回就state还是新state
return hasChanged ? nextState : state;
};
}

Redux 之 combineReducers(reducers)详解

Redux 源码浅析系列(二):combineReducer

redux 的 isPlainObject 源码

[Redux] redux之combineReducers的更多相关文章

  1. [Redux] Redux: Extracting Container Components -- AddTodo

    Code to be refactored: const AddTodo = ({ onAddClick }) => { let input; return ( <div> < ...

  2. Flux --> Redux --> Redux React 入门

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  3. [Redux] redux的概述

    redux 的概述 随着 javascript 单页应用的不断发展,javascript 需要管理比以往都要多的状态,管理不断变化的 state 非常困难,数据流动不断变的模糊不可预测,代码的开发与维 ...

  4. [React] 11 - Redux: redux

    Ref: Redux中文文档 Ref: React 讀書會 - B團 - Level 19 Redux 深入淺出 Ref: React+Redux 分享會 Ruan Yifeng, Redux 架构: ...

  5. Flux --> Redux --> Redux React 基础实例教程

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  6. Flux --> Redux --> Redux React 入门 基础实例使用

    本文的目的很简单,介绍Redux相关概念用法 及其在React项目中的基本使用 假设你会一些ES6.会一些React.有看过Redux相关的文章,这篇入门小文应该能帮助你理一下相关的知识 一般来说,推 ...

  7. 基于 Redux + Redux Persist 进行状态管理的 Flutter 应用示例

    好久没在 SegmentFault 写东西,唉,也不知道 是忙还是懒,以后有时间 再慢慢写起来吧,最近开始学点新东西,有的写了,个人博客跟这里同步. 一直都在自己的 React Native 应用中使 ...

  8. Redux API之combineReducers

    combineReducers(reducers) 随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分. combineReducers 辅助函 ...

  9. [React] 14 - Redux: Redux Saga

    Ref: Build Real App with React #14: Redux Saga Ref: 聊一聊 redux 异步流之 redux-saga  [入门] Ref: 从redux-thun ...

随机推荐

  1. Spring源码阅读 源码环境搭建(一)

    ring 源码阅读的搭建(一) 一 下载spring源码 进入官方网页:https://spring.io/projects/spring-framework 进入相关的github位置,下载zip包 ...

  2. Ubuntu环境下配置darknet

    本教程基于Linux物理机进行相关配置,要求物理机中包含N卡且Capbility>=3.0,小于3.0(Fermi架构)只允许配置cuda,不能配置使用Cudnn: 本教程分为: 1.安装NVI ...

  3. 3-2 Hadoop集群伪分布模式配置部署

    Hadoop伪分布模式配置部署 一.实验介绍 1.1 实验内容 hadoop配置文件介绍及修改 hdfs格式化 启动hadoop进程,验证安装 1.2 实验知识点 hadoop核心配置文件 文件系统的 ...

  4. 5 个免费的受欢迎的 SQLite 管理工具【申明:来源于网络】

    5 个免费的受欢迎的 SQLite 管理工具 包含内容: SQLite Expert – Personal Edition SQLite Expert 提供两个版本,分别是个人版和专业版.其中个人版是 ...

  5. LeetCode编程训练 - 滑动窗口(Sliding Window)

    滑动窗口基础 滑动窗口常用来解决求字符串子串问题,借助map和计数器,其能在O(n)时间复杂度求子串问题.滑动窗口和双指针(Two pointers)有些类似,可以理解为往同一个方向走的双指针.常用滑 ...

  6. Java设计模式之单例模式,笔记完整到不敢想象

    单例模式: 作用 保证一个类只有一个实例,并且提供一个访问该实例的全局访问入口 单例模式的常用 1.Windows的任务管理器2.Windows的回收站,也是一个单例应用3.项目中的读取配置文件的对象 ...

  7. 原来Java世界里也有这么多精彩的故事,学Java真有趣!

    大千世界,无所不有.这世上不光有人类世界,还有咱们的java世界.今天就由我这个实习导游带领你们了解了解咱们的java世界奇妙之处.   有一种暖男叫catch,有一种真爱叫try---catch,世 ...

  8. [Swift]LeetCode15. 三数之和 | 3Sum

    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find ...

  9. [Swift]LeetCode249.群组偏移字符串 $ Group Shifted Strings

    Given a string, we can "shift" each of its letter to its successive letter, for example: & ...

  10. 【Redis篇】Redis集群安装与初始

    一.前述   本文将单台节点不同端口模拟集群方式. 二.具体搭建 前提是安装好redis具体可参考http://www.cnblogs.com/LHWorldBlog/p/8463269.html 1 ...