【翻译向】走进 Redux 之高级篇

异步的 Actions

前几篇的教程里面,我们搭建了一个 todo 应用。这是一个完全同步的,每次一个 action 被 dispatched, state 都会即时更新。
解析来,我们要搭建一个不同的,异步的应用。它使用 Reddit API 来展示一个选中栏目的头条。

Actions

当你调用一个异步的 API 时,有两个至关重要的时刻:你开始调用的时刻,和你收到答复的时刻。

这两个时刻通常都会需要对应用的 state 做出变化;为此,你需要 dispatch 普通的将会被 reducers 同步处理的 actions. 通常,对于任意 API 请求,你将需要 dispatch 至少三次不同的 actions:

  • 一个通知 reducers 请求开始的 action
    reducers 可能通过切换一个 state 中的 isFetching flag 来处理这个 action. 用来告诉 UI 是时候显示一个等待标识了。
  • 一个通知 reducers 请求已经成功完成的 action
    reducers 可能通过把新数据合并到它控制的 state 中并重置 isFetching 来处理这个 action. UI 会隐藏等待标识,并展示获取到的数据。
  • 一个通知 reducers 请求失败的 action
    reducers 可能通过重置 isFetching 来处理这个 action. 另外,有些 reducers 可能想要存储这个错误信息,这样可以让 UI 展示出来。

可能需要在 actions 添加一个专用的 status 字段:

{type: 'FETCH_POSTS'}
{type: 'FETCH_POSTS', status: 'error', error: 'Oops'}
{typs: 'FETCH_POSTS', status: 'success', response: { ... }}

或者为它们定义单独的 types:

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

选择一个拥有 flags 的单独的 action type, 或者多个 actions types, 这都取决与你。多个 types 会有更少的犯错空间,但这不是一个问题,如果你使用 redux-actions 这样的辅助库来生成 action creator 和 reducers 的话。

异步的数据流

不使用 middlerware, Redux store 只提供同步的数据流。这是你通过 createStore() 得到的默认结果。

可以使用 applyMiddleware() 来增强 createStore(). 这不是必须的,但它可以让你通过一个便利的方法描述异步的 action 。

异步的 middleware 比如 redux-thunk 或 redux-promise 都包装了 store 的 dispatch() 方法,允许你 dispatch 除了 action 以外的内容,比如函数或者 Promise。你所使用的 middleware 可以以自己的方式解析你 dispatch 的任何内容,并继续传递 actions 给下一个 middleware。比如,支持 Promise 的 middleware 能够拦截 Promise,然后为每个 Promise 异步地 dispatch 一对 begin/end actions。

当数据流上最后一个 middleware dispatches 一个 actions 时,它必须是一个普通的对象。这是同步的 Redux 数据流开始的地方。

中间件(Middleware)

中间件提供一个第三方的扩展点,在 dispatching 一个 action 和 它到达 reducer 的中间时刻。人们使用中间件来打印日志,记录崩溃报告,调用一个异步的 API, 路由……
这里有一些例子来展示中间件的强大作用

理解中间件

中间件可以做很多事情,理解它是从哪来的非常重要。我们通过使用 logging 和 crash reporting 这俩个例子来展示一个使用中间件的思维过程。

问题: 日志记录

尝试#1:手动记录

最原始的解决方案就是每次在调用 store.dispatch(action) 时记录 action 和 下一个 state. 代码可能就是下面的样子:

let action = addTodo('Use Redux')
console.log('dispatching',action)
store.dispatch(action)
console.log('next state', store.getState())

这真的是一种很搓的办法。

尝试#2:包装 Dispatch

把 logging 提取到一个函数里面:

function dispatchAndLog(store,action){
console.log('dispatching',action)
store.dispatch(action)
console.log('next state',store.getState())
}

这样每次调用的时候通过使用这个函数来替换 store.dispatch()

dispatchAndLog(store,addTOdo('USe Redux'))

这样已经能解问题了,但是这样并不优雅。

尝试#3: Monkeypatching Dispatch

如果我们只在 store 里面替换 dispatch 函数呢? Redux 的 store 只是一个拥有几个方法的普通对象,所以我们可以改写 dispatch

let next = store.dispatch
store.dispath = fucntion dispatchAndLog (action){
console.log('dispatching',action)
let result = next(action)
console.log('next state',soter.getState())
return reslut
}

这样基本上已经达到了我们的妖要求,不论你在哪里 dispatch 一个 action, 它都保证会被记录下来。虽然我们通过 Monkeypatching 改写了 store 内部的方法,但暂时先这样。