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的函数详解的更多相关文章

  1. linux内核中send与recv函数详解

    Linux send与recv函数详解 1.简介 #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t n ...

  2. linux中fork()函数详解(原创!!实例讲解) (转载)

     一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不 ...

  3. (转)linux中fork()函数详解

    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...

  4. linux中fork()函数详解

    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...

  5. linux中fork()函数详解(转)

    转自:http://blog.csdn.net/jason314/article/details/5640969 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过 ...

  6. linux中fork()函数详解(原创!!实例讲解)

    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...

  7. Linux中fork()函数详解(转载)

    [原创地址]http://blog.csdn.net/jason314/article/details/5640969 [转载地址]http://www.cnblogs.com/bastard/arc ...

  8. linux中fork()函数详解(转)

    转自:http://blog.csdn.net/jason314/article/details/5640969 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过 ...

  9. Android总结篇系列:Activity中几个主要函数详解

    Activity作为Android系统中四大基本组件之一,包含大量的与其他的各大组件.intent.widget以及系统各项服务等之间的交互的函数.在此,本文主要选取实际项目开发中常用的,但完全理解又 ...

随机推荐

  1. 当遇到Mac的Excel或者Word老是重复崩溃的时候

    打开Number,新建文件然后导出为Excel.之后再用Excel打开,一切都OK了.

  2. &quot;Simple Factory&quot; vs &quot;Factory Method&quot; vs &quot;Abstract Factory&quot; vs &quot;Reflect&quot;

    ref: http://www.cnblogs.com/zhangchenliang/p/3700820.html 1. "Simple Factory" package torv ...

  3. 正则表达式的捕获组(capture group)在Java中的使用

    原文:http://blog.csdn.net/just4you/article/details/70767928 ------------------------------------------ ...

  4. 014 DNS

    解析主机名 Router>en Password: Router#config t Enter configuration commands, one per line.  End with C ...

  5. VS打包部署图文具体步骤及程序防卸载的制作(password验证卸载)

    1.  在vs2010 选择"新建项目->"其它项目类型"->" Visual StudioInstallerà "安装项目": ...

  6. jquery 联动 年月日

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>JQuery实例 - 生成年 ...

  7. LVS 负载均衡 (VS/DR模式 与 VS/TUN 模式)

    一.VS/DR模式 ①.客户端将请求发往前端的负载均衡器,请求报文源地址是CIP,目标地址为VIP. ②.负载均衡器收到报文后,发现请求的是在规则里面存在的地址,那么它将目标MAC改为了RIP的MAC ...

  8. PHP中的extract函数的用途 extract($_GET);extract($_POST)

    把客户端表单中的变量名取出来 addslashes -- 使用反斜线引用字符串 extract(addslashes($_POST)); --处理POST表单 把客户端<FORM METHOD= ...

  9. WebSocket服务端

    http://blog.csdn.net/qq_20282263/article/details/54310737 C# 实现WebSocket服务端 原创 2017年01月10日 09:22:50 ...

  10. “指定的SAS安装数据(sid)文件不能用于选定的SAS软件订单

    Sas安装指南,若拿到可用的sid文件,需要做如下修改. 使用新sid的时候,“指定的SAS安装数据(sid)文件不能用于选定的SAS软件订单”问题解决: 1.进入sas安装包,install_doc ...