Redux之combineReducers(reducers)详解

xiaoxiao2021-02-28  45

大家好,最近有点忙,忙什么呢?忙着学习一个新的框架Redux,那么这个框架主要是用来做什么的,这篇博客暂时不做介绍,这篇博客针对有一定Redux开发基础的人员,所以今天我讲的重点是Redux里面很重要的一个方法-combineReducers(reducers)。

(一)官网介绍

首先,按照惯例,这个方法是什么,干什么用,输入是什么,输出是什么,这些都是我们要了解的,所以我们先来看看官网是如何介绍它的,在后面的内容中我会给大家分析一下这个方法内部是如何实现的以及它的实现原理,慢慢来,不要慌~

combineReducers(reducers)

这里为了方便大家阅读,直接上中文解释

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

combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。

合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。

最终,state 对象的结构会是这样的:

{ reducer1: ... reducer2: ... } 1234

通过为传入对象的 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是如何工作的,那么我们以后的工作才更好的展开,不至于出现了问题而不知道如何解决。

转载请注明原文地址: https://www.6miu.com/read-2300017.html

最新回复(0)