Redux 中的CombineReducer的函数详解
combineReducers(reducers)
随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分。
combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。
合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。
最终,state 对象的结构会是这样的:
{
reducer1: ...
reducer2: ...
}
通过为传入对象的 reducer 命名不同来控制 state key 的命名。例如,你可以调用 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 将 state 结构变为 { todos, counter }。
通常的做法是命名 reducer,然后 state 再去分割那些信息,因此你可以使用 ES6 的简写方法:combineReducers({ counter, todos })。这与 combineReducers({ counter: counter, todos: todos }) 一样。
Flux 用户使用须知
本函数可以帮助你组织多个 reducer,使它们分别管理自身相关联的 state。类似于 Flux 中的多个 store 分别管理不同的 state。在 Redux 中,只有一个 store,但是 combineReducers 让你拥有多个 reducer,同时保持各自负责逻辑块的独立性。
参数
reducers (Object): 一个对象,它的值(value) 对应不同的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数需要满足的规则。
之前的文档曾建议使用 ES6 的 import * as reducers 语法来获得 reducer 对象。这一点造成了很多疑问,因此现在建议在 reducers/index.js 里使用 combineReducers() 来对外输出一个 reducer。下面有示例说明。
返回值
(Function):一个调用 reducers 对象里所有 reducer 的 reducer,并且构造一个与 reducers 对象结构相同的 state 对象。
注意
本函数设计的时候有点偏主观,就是为了避免新手犯一些常见错误。也因些我们故意设定一些规则,但如果你自己手动编写根 redcuer 时并不需要遵守这些规则。
每个传入 combineReducers 的 reducer 都需满足以下规则:
所有未匹配到的 action,必须把它接收到的第一个参数也就是那个 state 原封不动返回。
永远不能返回 undefined。当过早 return 时非常容易犯这个错误,为了避免错误扩散,遇到这种情况时 combineReducers 会抛异常。
如果传入的 state 就是 undefined,一定要返回对应 reducer 的初始 state。根据上一条规则,初始 state 禁止使用 undefined。使用 ES6 的默认参数值语法来设置初始 state 很容易,但你也可以手动检查第一个参数是否为 undefined。
虽然 combineReducers 自动帮你检查 reducer 是否符合以上规则,但你也应该牢记,并尽量遵守。
示例
reducers/todos.js
export default function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
reducers/counter.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'
export default combineReducers({
todos,
counter
})
App.js
import { createStore } from 'redux'
import reducer from './reducers/index'
let store = createStore(reducer)
console.log(store.getState())
// {
// counter: 0,
// todos: []
// }
store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
console.log(store.getState())
// {
// counter: 0,
// todos: [ 'Use Redux' ]
// }
小贴士
本方法只是起辅助作用!你可以自行实现不同功能的 combineReducers,甚至像实现其它函数一样,明确地写一个根 reducer 函数,用它把子 reducer 手动组装成 state 对象。
在 reducer 层级的任何一级都可以调用 combineReducers。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer,甚至更多层。
(二)内部分析
看了第一部分官网的介绍,大家都应该知道了它是如何使用的,但是作为开发人员,我觉得我们有必要思考一下它里面的究竟是如何实现的呢?下面我们来分析一下。
第一步,我们知道reducer 就是一个函数,接收旧的 state 和 action,返回新的 state。知道这点很重要!
第二步,我们来看combineReducers(reducers)方法,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。
那么combineReducers(reducers)返回的就是一个最终的reducer,reducer里面会返回新的state。下面我们结合代码来看:
const rootReducer = combineReducers({
reducer1,
reducer2,
reducer3,
...
});
这里根reducer暂且起名为rootReducer,它就是通过combineReducers(reducers)返回的。那么combineReducers(reducers)是如何做的呢?
第三步,我们自己来写一个function,来代替combineReducers(reducers)方法,从而来摸索内部是如何实现的,新建一个方法,如下:
var combineReducers1 = function(obj){
//内部具体代码
}
这个方法返回的肯定是一个reducer,那么我们先写出来:
var combineReducers1 = function(obj){
//内部具体代码
//返回最终的reducer
return reducer;
}
我们应该能想到下一步该做什么了,既然返回一个reducer,那么我们就要创建一个reducer了,方法接收两个参数,一个是state,一个是action,继续:
var combineReducers1 = function(obj){
//内部具体代码
function reducer(state,action){
//reducer具体逻辑
}
//返回最终的reducer
return reducer;
}
reducer最终返回的是一个state,我们接着写:
var combineReducers1 = function(obj){
//内部具体代码
var finalState = {};
function reducer(state,action){
//reducer具体逻辑
//返回state
return finalState;
}
//返回最终的reducer
return reducer;
}
那么现在想一想,我们传入的object对象,实际上就是我们传入的所有的reducer方法的集合,实际上里面做的就是分别调用每个reducer方法,将每个reducer方法作为value值赋予我们传入object对象的属性名,通过JavaScript遍历对象获取属性名赋值的方法,我们可以得到最关键的代码,接着往下写:
var combineReducers1 = function(obj){
//内部具体代码
var finalState = {};
function reducer(state,action){
//reducer具体逻辑
for (var p in obj) {
//根据key属性值调用function(state.属性名,action)
finalState[p] = obj[p](state[p], action);
}
//返回state
return finalState;
}
//返回最终的reducer
return reducer;
}
因为我们reducer()方法里传入的state,其实是根state,所以得根据属性名来获取对应的reducer上的state,到这里,关于combineReducers()方法的具体实现我们已经分析完了,了解了combineReducers是如何工作的,那么我们以后的工作才更好的展开,不至于出现了问题而不知道如何解决。
Redux 中的CombineReducer的函数详解的更多相关文章
- linux内核中send与recv函数详解
Linux send与recv函数详解 1.简介 #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t n ...
- linux中fork()函数详解(原创!!实例讲解) (转载)
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不 ...
- (转)linux中fork()函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- linux中fork()函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- linux中fork()函数详解(转)
转自:http://blog.csdn.net/jason314/article/details/5640969 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过 ...
- linux中fork()函数详解(原创!!实例讲解)
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- Linux中fork()函数详解(转载)
[原创地址]http://blog.csdn.net/jason314/article/details/5640969 [转载地址]http://www.cnblogs.com/bastard/arc ...
- linux中fork()函数详解(转)
转自:http://blog.csdn.net/jason314/article/details/5640969 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过 ...
- Android总结篇系列:Activity中几个主要函数详解
Activity作为Android系统中四大基本组件之一,包含大量的与其他的各大组件.intent.widget以及系统各项服务等之间的交互的函数.在此,本文主要选取实际项目开发中常用的,但完全理解又 ...
随机推荐
- 当遇到Mac的Excel或者Word老是重复崩溃的时候
打开Number,新建文件然后导出为Excel.之后再用Excel打开,一切都OK了.
- "Simple Factory" vs "Factory Method" vs "Abstract Factory" vs "Reflect"
ref: http://www.cnblogs.com/zhangchenliang/p/3700820.html 1. "Simple Factory" package torv ...
- 正则表达式的捕获组(capture group)在Java中的使用
原文:http://blog.csdn.net/just4you/article/details/70767928 ------------------------------------------ ...
- 014 DNS
解析主机名 Router>en Password: Router#config t Enter configuration commands, one per line. End with C ...
- VS打包部署图文具体步骤及程序防卸载的制作(password验证卸载)
1. 在vs2010 选择"新建项目->"其它项目类型"->" Visual StudioInstallerà "安装项目": ...
- jquery 联动 年月日
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JQuery实例 - 生成年 ...
- LVS 负载均衡 (VS/DR模式 与 VS/TUN 模式)
一.VS/DR模式 ①.客户端将请求发往前端的负载均衡器,请求报文源地址是CIP,目标地址为VIP. ②.负载均衡器收到报文后,发现请求的是在规则里面存在的地址,那么它将目标MAC改为了RIP的MAC ...
- PHP中的extract函数的用途 extract($_GET);extract($_POST)
把客户端表单中的变量名取出来 addslashes -- 使用反斜线引用字符串 extract(addslashes($_POST)); --处理POST表单 把客户端<FORM METHOD= ...
- WebSocket服务端
http://blog.csdn.net/qq_20282263/article/details/54310737 C# 实现WebSocket服务端 原创 2017年01月10日 09:22:50 ...
- “指定的SAS安装数据(sid)文件不能用于选定的SAS软件订单
Sas安装指南,若拿到可用的sid文件,需要做如下修改. 使用新sid的时候,“指定的SAS安装数据(sid)文件不能用于选定的SAS软件订单”问题解决: 1.进入sas安装包,install_doc ...