本文参考阮一峰老师的文章,着重从数据流的角度分析 redux

redux

同步数据流

参照图例,这是一次用户行为的数据流图。

  • (1) 用户操作 View

  • (2)(3) View 通过 Action Creator 发出相应的 Action

    • Action Creator 就是一个 Action 工厂,统一管理所有的 Action。让代码更好管理,互用性更强。
  • (4) Store 通过 dispatch 函数获取相应的 Action,并且触发 Reduer 计算新的 State

    • dispatch 之所以可以自动触发 Reducer,是因为在生成 Store 的时候就已经绑定好了。

      1
      2
      import { createStore } from 'redux';
      const store = createStore(reducer);
    • dispatch 的接口只接收对象。

  • (5) Reduer 接收现在的 State,以及 Action。做出相应的状态变化计算,得到新的 State。并且通过 Storesubscibe 监听 State 的变化,并回调对应的 Listener 函数。

    • Reducer 是状态机的核心,定义了状态转移的计算方法。 也正是因为这些 Action 是我们手动绑定并进行处理,保证了数据流的单向性。
    • 当然对于比较繁琐的 Reducer 的设计也有更好的设计模式,比如提供了 combineReducers 函数,详细用法可见阮一峰老师的文档。
    • 对应的 Listener 函数也是在申明的时候已经做好了绑定。
      1
      2
      3
      4
      import { createStore } from 'redux';
      const store = createStore(reducer);

      store.subscribe(listener);
  • (6)Listener 中获取现在的 State 并用它重新渲染 View

    • 可以通过 store.getState() 获取现在的 State
      1
      2
      3
      4
      function listerner() {
      let newState = store.getState();
      component.setState(newState);
      }

异步数据流

由于 Reduxstore.dispatch 的接口要求很严格,只能传递对象类型的 Action,所以这里我们需要先引入中间件来完成我们理想的设计。

中间件

  • (4) 在原第4步过程的基础上,我们引入中间件。 Action 会先被中间件逐步拦截处理以后传递给 Reducer。并且多个中间件是支持通过 applyMiddlewares() 函数来连接在一起。

异步实现

  • (3) 异步数据流首先对传统的仅发送对象的 Action 做了修改,这里发送函数类型的 Action,在这个返回函数当中先发送了一个异步开始的对象 Action ,在结束的时候再发送异步成功/失败的对象 Action

    这里是我纠结比较久的地方,最开始我很不能接受这种将IO操作放在 Action 里的设计,因为我觉得像这样的数据处理相关的操作是应该放在 Reducer 里的。后来我和室友讨论了以后,有了一些新的想法:

    • 首先异步操作是应该被放在网络层(或者叫IO层),而 Reducer 是担任数据计算的任务,所以把异步操作放在 Reducer 里也是一种不适当的分层。
    • 然后因为 Creator 的任务本来就很轻松,只用生成一些 Action 对象,所以这边把网络层放入其中也比较合适。
    • 最后这种设计其实也没有和最初的思想违背,Creator 还是只是生成一些 Action,而并没有执行这些 Action

    以上只是个人看法,欢迎拍砖讨论。

  • 至于实现的方案就有很多了,比如阮一峰老师文章里的 redux-thunkredux-promise,还有现在比较流行的基于 Generatorredux-sage。这里就不一一赘述。

React-Reduce

当直接使用 React-Reduce 的时候,需要按照规定的范式将组件拆分为 UI组件容器组件。详细可见阮一峰老师的文档,这里主要介绍使用了 React-Reduce 以后对于我们的数据流有哪些影响。

  • (1)(2) 通过 mapDispatchToProps 函数,可以设置哪些用户的操作会被当做 Action,并且当做哪个 Action 传递给 Store,也就是我们不需要在第一步中 Hard code 一些动作到 Action 的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const mapDispatchToProps = (
    dispatch,
    ownProps
    ) => {
    return {
    onClick: () => {
    dispatch({
    type: 'SET_VISIBILITY_FILTER',
    filter: ownProps.filter
    });
    }
    };
    }
  • (6) 通过 mapStateToProps 函数,可以进行 StateUI组件prop 的映射。并对 View 进行重新渲染,也就是说我们不需要在第6步当中再写一些获取现在 State 做渲染的工作。(非常适合做一些过滤、分析、或者担任数据的组织层)

    1
    2
    3
    4
    5
    const mapStateToProps = (state) => {
    return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
// 根据是否完成进行过滤
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
default:
throw new Error('Unknown filter: ' + filter)
}
}

Comments

⬆︎TOP