1.异步Action

Action

当调用异步 API 时,有两个非常关键的时刻:发起请求的时刻,和接收到响应的时刻(也可能是超时)。
这两个时刻都可能会更改应用的 state;为此,你需要 dispatch 普通的同步 action。一般情况下,每个 API 请求都需要 dispatch 至少三种 action:

  • 一种通知 reducer 请求开始的 action。
    对于这种 action,reducer 可能会切换一下 state 中的 isFetching 标记。以此来告诉 UI 来显示加载界面。
  • 一种通知 reducer 请求成功的 action。
    对于这种 action,reducer 可能会把接收到的新数据合并到 state 中,并重置 isFetching。UI 则会隐藏加载界面,并显示接收到的数据。
  • 一种通知 reducer 请求失败的 action。
    对于这种 action,reducer 可能会重置 isFetching。另外,有些 reducer 会保存这些失败信息,并在 UI 里显示出来。

为了区分这三种 action,可能在 action 里添加一个专门的 status 字段作为标记位:

  1. { type: 'FETCH_POSTS' }
  2. { type: 'FETCH_POSTS', status: 'error', error: 'Oops' }
  3. { type: 'FETCH_POSTS', status: 'success', response: { ... } }

又或者为它们定义不同的 type:

  1. { type: 'FETCH_POSTS_REQUEST' }
  2. { type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
  3. { type: 'FETCH_POSTS_SUCCESS', response: { ... } }

究竟使用带有标记位的同一个 action,还是多个 action type 呢,完全取决于你。这应该是你的团队共同达成的约定。使用多个 type 会降低犯错误的机率,但是如果你使用像 redux-actions 这类的辅助库来生成 action 创建函数和 reducer 的话,这就完全不是问题了。
无论使用哪种约定,一定要在整个应用中保持统一。

异步Action创建函数

同步action我们都知道,就是正常的action,如何把 同步 action 创建函数和网络请求结合起来呢?标准的做法是使用 Redux Thunk 中间件。要引入 redux-thunk 这个专门的库才能使用。通过使用指定的 middleware,action 创建函数除了返回 action 对象外还可以返回函数。这时,这个 action 创建函数就成为了 thunk。
当 action 创建函数返回函数时,这个函数会被 Redux Thunk middleware 执行。这个函数并不需要保持纯净;它还可以带有副作用,包括执行异步 API 请求。这个函数还可以 dispatch action,就像 dispatch 前面定义的同步 action 一样。

  1. // 来看一下我们写的第一个 thunk action 创建函数!
  2. // 虽然内部操作不同,你可以像其它 action 创建函数 一样使用它:
  3. // store.dispatch(fetchPosts('reactjs'))
  4. export function fetchPosts(subreddit) {
  5. // Thunk middleware 知道如何处理函数。
  6. // 这里把 dispatch 方法通过参数的形式传给函数,
  7. // 以此来让它自己也能 dispatch action。
  8. return function (dispatch) {
  9. // 首次 dispatch:更新应用的 state 来通知
  10. // API 请求发起了。
  11. dispatch(requestPosts(subreddit))
  12. // thunk middleware 调用的函数可以有返回值,
  13. // 它会被当作 dispatch 方法的返回值传递。
  14. // 这个案例中,我们返回一个等待处理的 promise。
  15. // 这并不是 redux middleware 所必须的,但这对于我们而言很方便。
  16. return fetch(`http://www.subreddit.com/r/${subreddit}.json`)
  17. .then(
  18. response => response.json(),
  19. // 不要使用 catch,因为会捕获
  20. // 在 dispatch 和渲染中出现的任何错误,
  21. // 导致 'Unexpected batch number' 错误。
  22. // https://github.com/facebook/react/issues/6895
  23. error => console.log('An error occurred.', error)
  24. )
  25. .then(json =>
  26. // 可以多次 dispatch!
  27. // 这里,使用 API 请求结果来更新应用的 state。
  28. dispatch(receivePosts(subreddit, json))
  29. )
  30. }
  31. }

2.异步数据流

默认情况下,createStore() 所创建的 Redux store 没有使用 middleware,所以只支持 同步数据流
你可以使用 applyMiddleware() 来增强 createStore()
redux-thunkredux-promise 这样支持异步的 middleware 都包装了 store 的 dispatch() 方法,以此来让你 dispatch 一些除了 action 以外的其他内容,例如:函数或者 Promise。你所使用的任何 middleware 都可以以自己的方式解析你 dispatch 的任何内容,并继续传递 actions 给下一个 middleware。比如,支持 Promise 的 middleware 能够拦截 Promise,然后为每个 Promise 异步地 dispatch 一对 begin/end actions。
当 middleware 链中的最后一个 middleware 开始 dispatch action 时,这个 action 必须是一个普通对象。这是 同步式的 Redux 数据流 开始的地方(译注:这里应该是指,你可以使用任意多异步的 middleware 去做你想做的事情,但是需要使用普通对象作为最后一个被 dispatch 的 action ,来将处理流程带回同步方式)。