简介

基本概念

随着单页面应用需求越来越复杂,应用需要管理的状态也变得越来越多。这里的状态不仅包括从服务器端获取的数据,还包括本地创建的数据,同样也包括反映 UI 状态的数据,例如当前路由的位置、选中的标签、弹出框的控制等。Redux 通过一系列约定的规范将修改应用状态的步骤标准化,让应用状态的管理不再错综复杂,而是如同一根长线般清晰。

下面通过一个简单的例子阐述 Redux 的核心概念。本章项目源代码的目录为 /chapter-08/todos-redux。假设我们要开发一个待办事项 todos 的应用,用一个 JavaScript 对象描述这个应用的状态:

{
  "todos": [{
    "text": 'Learn React',
    "completed": true
  }, {
    "text": 'Learn Redux',
    "completed": false
  }],
  "visibilityFilter": 'SHOW_COMPLETED'
}

todos 包含所有的事项,包括已经完成的和尚未完成的,根据 completed 属性区分该事项是否已经完成;visibilityFilter 是一个过滤条件,表示当前界面上应该显示哪些事项。这个对象需要被视为只读对象,当需要修改应用状态时,必须发送一个 action,action 描述应用状态应该如何修改,其实 action 只是一个普通的 JavaScript 对象。例如,当新增一个待办事项时,可以发送下面的 action:

{ "type": 'ADD_TODO', "text": 'Learn MobX' }

type 代表 action 的类型,此处 ADD_TODO 表示要新增一个待办事项,text 包含新增事项的内容。类似地,如果要让界面显示所有的事项,可以发送下面的 action:

{ "type": 'SET_VISIBILITY_FILTER', "filter": 'SHOW_ALL' }

注意,action 的结构并不是确定的(但必须包含 type 字段),不同的人有不同的描述习惯,例如,可以通过 action 描述新增一个待办事项:

{ "type": 'ADD', "data": 'Learn MobX' }

只要保证你的程序知道如何解析定义的 action 即可。那么,如何解析 action 呢?Redux 通过 reducer 解析 action。reducer 是一个普通的 JavaScript 函数,接收 action 为参数,然后返回一个新的应用状态 state。例如,这里我们定义一个 reducer(省略处理 state 的逻辑代码):

function todoApp(state={}, action) {
    switch(action.type) {
        case SET_VISIBILITY_FILTER:
            // return new state
        case ADD_TODO:
            // return new state
        case TOGGLE_TODO:
            // return new state
        default:
            return state
    }
}

todoApp 就是管理应用全局 state 的 reducer,todoApp 每次返回的 state 的结构就是最初定义的应用状态的结构:

{
    "todos": [...],
    "visibilityFilter": ...
}

这一系列过程描述了 Redux 的基本概念。Redux 的主要思想就是描述应用的状态如何根据 action 进行更新,Redux 通过提供一系列 API 将这一主要思想的落地实施进行标准化和规范化。但就 Redux 的使用者而言,编写的绝大部分代码都是普通的 JavaScript 代码,只有在个别地方会用到 Redux 的 API 而已。

三大原则

Redux 应用需要遵循三大原则,否则程序很容易出现难以察觉的问题。

唯一数据源

Redux 应用只维护一个全局的状态对象,存储在 Redux 的 store 中。唯一数据源是一种集中式管理应用状态的方式,便于监控任意时刻应用的状态和调试应用,减少出错的可能性。

保持应用状态只读

在任何时候都不能直接修改应用状态。当需要修改应用状态时,必须发送一个 action,由这个 action 描述如何修改应用状态。这一看似烦琐的修改状态的方式实际上是 Redux 状态管理流程的核心,保证了在大型复杂应用中状态管理的有序进行。

应用状态的改变通过纯函数完成

action 表明修改应用状态的意图,真正对应用状态做修改的是 reducer。reducer 必须是纯函数,所以 reducer 在接收到 action 时,不能直接修改原来的状态对象,而是要创建一个新的状态对象返回。

纯函数是指满足以下两个条件的函数:

  1. 对于同样的参数值,函数的返回结果总是相同的,即该函数结果不依赖任何在程序执行过程中可能改变的变量。

  2. 函数的执行不会产生副作用,例如修改外部对象或输出到 I/O 设备。