Redux

Redux 是一个状态管理框架,
但我觉得它同时也是一门语言,因为它有自己的一套行事规则。
Redux 代码库本身很小,核心概念也很简洁明了,但是它的 “语言” 却不寻常,
加之 Redux 已经有一段历史了,需要了解它的一个技术变革过程,这样才能更好的理解它和使用它。

一、了解 Redux

1. 动机

前端逻辑变得越来越复杂,

这种复杂性很难处理,因为人类大脑很难推理两个概念:突变和异步。

React 通过删除异步和直接操作DOM来解决视图层中的这个问题。

但是,管理数据的状态由开发者决定。这就是 Redux 进入的地方。

2. 三大原则(核心内容)

1). 单一数据源

应用的全局状态存储在单个store内的对象树中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18

console.log(store.getState())

/* Prints
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
*/

2). 状态是只读的

改变状态的唯一方法是发出一个Action动作,它是描述 “发生了什么” 的字面量对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

store.dispatch({
  type: 'COMPLETE_TODO',
  index: 1
})

store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
})

3). 使用纯函数进行更改(状态副本)

编写纯 reducers 函数,它接受 前一个状态 和一个 动作,然后 返回下一个状态。记住要返回新的状态对象,而不是改变以前的状态。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

function visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: true
          })
        }
        return todo
      })
    default:
      return state
  }
}

import { combineReducers, createStore } from 'redux'
const reducer = combineReducers({ visibilityFilter, todos })
const store = createStore(reducer)

3. 词汇表

1). State 状态

type State = any

由 store 管理并由 getState() 获取。

不要在 State 中放任何不能轻易转换成 JSON 的东西,应该尽最大努力保持状态可序列化。

2). Action 动作

type Action = Object

必须有一个 type 字段来定义 action 的类型。类型可以定义为常量并从另一个模块导入。

3). Reducer 减速器(最重要的概念)

type Reducer<S, A> = (state: S, action: A) => S

Reducer 用于将值的集合减少为单个值,并不是 Redux 独有的,它是函数式编程中的一个基本概念。例如 Array.prototype.reduce()

给定前一个state和一个action,Reducer 计算一个新state。它必须是纯函数 —— 对于给定的输入返回完全相同的输出的函数。

不要将 API 调用放入 reducer。

4). Dispatch 动作调度

type BaseDispatch = (a: Action) => Action
type Dispatch = (a: Action | AsyncAction) => any

区分一般调度中间件调度

一般调度是同步的,中间件调度往往是异步的。

5). Action creator 动作创建器

type ActionCreator<A, P extends any[] = any[]> = (...args: P) => Action | AsyncAction

创建 action 的方法。

如果 action 创建器需要读取当前状态、执行 API 调用或引起副作用(如路由转换),它应该返回异步 action 而不是一般 action。

6). async Action 异步动作

type AsyncAction = any

不会立即传递给 reducer,而是在 异步action 转换为 同步action 后才触发 dispatch。

7). Middleware 中间件

type MiddlewareAPI = { dispatch: Dispatch, getState: () => State }
type Middleware = (api: MiddlewareAPI) => (next: Dispatch) => Dispatch

中间件是一个高阶函数,它通常将异步action转化为action。

它在调度一个action和它到达reducer的那一刻之间提供了一个第三方扩展点。

通常使用中间件进行日志记录、崩溃报告、与异步 API 通信、路由等。

8). Store

type Store = {
  dispatch: Dispatch //基本调度函数
  getState: () => State //返回Store的当前状态
  subscribe: (listener: () => void) => () => void //注册要在状态更改时调用的函数
  replaceReducer: (reducer: Reducer) => void //可用于实现热重载和代码拆分。很少用到。
}

store 是保存应用状态树的对象。

Redux 应用中应该只有一个 store,因为逻辑发生在 reducer 级别。

9). Store creator 创建器

type StoreCreator = (reducer: Reducer, preloadedState: ?State) => Store

创建 store 的函数

10). Store enhancer 增强器

type StoreEnhancer = (next: StoreCreator) => StoreCreator

高阶函数,它组合store创建器以返回新的增强store创建器。Redux 中间件实现本身就是一个store增强器。

store增强器与 React 中的高阶组件的概念大致相同,有时也称为“组件增强器”。

因为它不是实例,而是函数的普通对象集合,所以可以轻松创建和修改副本,而无需改变原始store。


二、使用 Redux