今天是 Redux 的第三个系列篇了!😊

在前面 Store 与 State 一专栏,我们详细的剖析了用于生成 Store 的 createStore 方法:调用该方法可以接受表示 State 修改逻辑的纯函数 reducer、Store 的初始数据状态 preloadedState 和 State 增强中间件的 enhancer 中的一个或多个参数。调用完 createStore 方法,我们会得到整个应用的唯一 Store。

通过这个唯一的 Store,我们可以修改 Store 里面的数据状态 State( store.dispatch(action) ),订阅 State 数据状态的变化以作出响应( store.subscribe(listener) ),获取应用的最新 State 数据状态( store.getState() )和在必要的场景下替换修改 Store 里面数据状态 State 的纯函数逻辑( store.replaceReducer(newReducer) )。

还是那幅图!

image.png

Action

上面我们再次把 Store 的核心梳理了一下,发现 Redux 提供了唯一修改 State 的方法 —— dispatch。在调用 dispatch 发起修改状态数据 State 的时候,它会接受一个用于描述当前动作的 action。

这个 action 并没有什么复杂之处,从始至终(从 Flux 到 Redux)都只是被规范定义为一个普通的 JavaScript 对象。为了规范这个对象的定义,社区有一套专门的规范,该规范指定 action 对象必须包含 type 属性,用来指明当前发起的动作匹配的修改逻辑(⚠️注意:由于 type 属性值是一个常量,所以建议定义成全大写,在大型应用场景下建议将这些定义提取到单独的文件进行管理,以便得到复用)。如果在修改数据状态时,还需要额外的参数,你还可以传递 payload 属性。如果你的业务场景还需要其他任何的属性,你也进行传递。

还记得我们在前一专栏给出的 reducer 的示例代码吗:

  1. function counter(state = 0, action) {
  2. switch (action.type) {
  3. case 'INCREMENT':
  4. return state + 1
  5. case 'DECREMENT':
  6. return state - 1
  7. default:
  8. return state
  9. }
  10. }

counter 函数就是一个 reducer 函数,它会接收当前的数据状态 state 和调用 dispatch 时发送的 action,并根据 action 的 type 属性值匹配到对应的修改逻辑,并对当前的数据状态 state 进行修改(⚠️注意:这里不是直接修改,而是返回新的值)。说了这么多,我最终想表明的是:action 的定义应该和对应的 reducer 逻辑进行匹配,否则后面通过 dispatch 发送 action 并不会发生效应,而是返回旧的数据状态。
**
针对上面的 reducer 函数,我们可以定义如下的 action:

  1. // 用于返回 state 加 1 的数据状态
  2. const action = {
  3. type: 'INCREMENT',
  4. }
  5. // 用于返回 state 减 1 的数据状态
  6. const action = {
  7. type: 'DECREMENT',
  8. }
  9. // 用于返回旧的 state 数据状态,此时并不应该引起 view 重新渲染,因为状态没有更新
  10. const action = {
  11. type: '', // 除 INCREMENT 和 DECREMENT 之外的任意值
  12. }
  13. // 如果你的 reducer 需要额外的参数
  14. const action = {
  15. type: 'DECREMENT',
  16. payload: {
  17. count: 1
  18. }
  19. }

Action Creator

在大型应用场景下,如果我们像上面那种分别定义的方式会写很多重复的代码片段。作为程序猿,懒惰是我们制作工具的源泉。于是我们可以通过定义 actionCreator 的形式,减少冗余片段代码的出现。

在大多数应用场景中,对于添加数据,我们都会这样定义 action:

  1. const action = {
  2. type: 'ADD_DATA',
  3. payload: [对应的数据]
  4. }

这样的 action 定义肯定会在很多逻辑中得到复用,他们的 type 属性值都是一样的,不同的只是需要传递的附加参数 payload,所以我们完全可以抽取成 actionCreator:

  1. const ADD_DATA = 'ADD_DATA'
  2. function addData(data) {
  3. return {
  4. type: ADD_TODO,
  5. payload: data
  6. }
  7. }

我们使用的时候就会像这样直接调用 addData 方法获取 action:

  1. dispatch(addData('littleLane'))

如果你觉得这样的包装还不够,你还可以做得更彻底一点:

  1. const ADD_DATA = 'ADD_DATA'
  2. function addData(data) {
  3. dispatch({
  4. type: ADD_TODO,
  5. payload: data
  6. })
  7. }

具体使用什么方式,你可以根据自己的实际业务场景进行规划。

总结

action 的定义只是一个普通的对象,用来描述一个动作,一个修改 Store 里面数据状态的动作,它会通过 dispatch 发射到 reducer,然后在 reducer 逻辑中根据 action 中的 type 属性值进行数据状态的修改操作。在社区中一套专门的用于定义 action 的规范,这个规范指明 type 是必传属性,如果你的 reducer 数据状态修改逻辑需要额外的参数,你还可以传递 payload 属性值。

关于 action 和 actionCreator 的知识就说到这里了,下一专栏将会讲到数据状态修改逻辑纯函数 Reducer。