Skip to the content.

Redux核心设计模式:所有逻辑都集中在一个单独的reducer函数中,并且执行这些逻辑的唯一方式就是传给 Redux 一个能够描述当时情景的普通对象(plain object)。Redux store 调用这些逻辑函数,并传入当前的 state tree 以及这些描述对象,返回新的 state tree,接着 Redux store 便开始通知这些订阅者(subscriber)state tree 已经改变。

数据store代表整体结构的状态集,依赖于此状态集的 发布订阅 模式用于解耦actions和executes,将 动作执行 分开处理。描述actions用的是返回普通对象的函数,不同actions之间本就是相互独立解耦的。而描述所有executes用的是一整个函数,单个action触发的对应execute在里面被限制性的封装成为函数,称为reducer,然后传给Array.prototype.reduce(reducer[, initialValue]),限定为签名(preState, action) => newState 的纯函数结构,每个reducer函数处理状态集store的一部分,reducer函数的组织严格依赖于数据存储。

这里需要明确一点,初期特别倾向于将组件模块的组织与reducer函数组织结构完全进行类似的等同,实质上,这两者之间没有任何直接关联。第一,组件模块的结构组织可以异常灵活,自由组合嵌套,组件是数据消费者,只需使用中间件订阅所需数据。第二,reducer的组织总结出以下模式,reducer是数据的生产者,每个reducer函数职责是更新整体数据的一部分。

Reducer和State的基本结构

整个应用只有一个单一reducer。 无脑组织整个reducer的所有逻辑在单一函数的话,大概是如下典型结构的样子:

// state默认值初始化initialValue
function reducer(state = initialValue, action) {
  switch (action.type) {
    case 'INCREMENT':
      return newState(state, action); // 一段逻辑处理函数
    case 'DECREMENT':
      return anotherState(state, action); //  另一段逻辑处理函数
    default:
      return state;
  }
}

整个应用只有也一个单一store。store数据state中的顶层状态树通常是一个Plain Object即普通对象,就顶层设计来说,组织顶层对象数据常用方式是将数据划分为子树,每个顶层key对应着和 特定域 或者 切片 相关联的数据。无脑组织整个store的所有数据在单一对象上的话,就是每一层都用划分子树的这种方式,典型结构就是JSON格式。

大多数应用会处理多种数据类型,通常可以分为以下三类(我们以redux典型example TODOs为例):

Store 代表着应用核心,因此应该用域数据(Domain data)和应用状态数据(App state)定义 State,而不是用 UI 状态(UI state)。举个例子,state.leftPane.todoList.todos 这样的结构就是一个坏主意,因为整个应用的核心是 “todos” 而不仅仅是 UI 的一个模块。 todos 这个切片才应该是 state 结构的顶层。

UI 树和状态树之间很少有 1 对 1 的关系。除非你想明确的跟踪你的 Redux Store 中存储的 UI 数据的各个方面,但即使是这样,UI 数据的结构和域数据的结构也是不一样的。

👤认为一般的,不用全局追踪的UI状态即不存在需要组件间通信的状态数据,最好放于local state(定义数据),置于redux外部,在组件内部用setState进行更新(管理状态),由组件内部获得更好的封装。

一个典型的应用state大致会长这样:

{
    domainData1 : {},
    domainData2 : {},
    appState1 : {},
    appState2 : {},
    ui : {
        uiState1 : {},
        uiState2 : {},
    }
}

拆分Reducer逻辑

如上所述的典型reducer函数结构,函数复杂度于action.type种类成正相关,由应用复杂度决定的。拆分的过程叫 函数分解(functional decomposition) 。拆分出的短小的新的函数通常分为三类:

redux库工具函数combineReducers加上 slice reducer 配合状态树key-value结构是较为常用普遍的,更新逻辑被委托在基于普通对象结构的state切片的各个slice reducer 函数中。

不要将每一层的数据结构都臆想成普通对象,全局只出现了 slice reducer 函数以及官方提供的工具函数combineReducer。事实上,应充分利用 higher-order reducer slice reducer case function 三种方式拆分逻辑,才可以将混杂在各种数据结构中的rootReducer函数分解为耦合度低的各类函数。

Highter-order reducer进阶