Redux发起API请求

Redux的API请求应该放在什么位置,redux的文档Advanced一节中进行了比较详细的描述,一个很直观的做法是将API请求直接放在action creator中(用redux-thunk中间件来执行异步操作)。比如


export function loadPosts(userId) {
  // Interpreted by the thunk middleware:
  return function (dispatch, getState) {
    let { posts } = getState()
    if (posts[userId]) {
      // There is cached data! Don't do anything.
      return
    }

    dispatch({
      type: 'LOAD_POSTS_REQUEST',
      userId
    })

    // Dispatch vanilla actions asynchronously
    fetch(`http://myapi.com/users/${userId}/posts`).then(
      response => dispatch({
        type: 'LOAD_POSTS_SUCCESS',
        userId,
        response
      }),
      error => dispatch({
        type: 'LOAD_POSTS_FAILURE',
        userId,
        error
      })
    )
  }
}

但是这样有个明显的问题,每个不同的API都需要写很多很类似的代码,稍不注意就容易写错。redux文档Reducing Boilerplate一节中提到了一种办法来缓解这个问题,那就是api middleware。

API middleware

使用了Reducing Boilerplate中提到的api middleware之后,action creator就可以写成这样


export function loadPosts(userId) {
  return {
    // Types of actions to emit before and after
    types: ['LOAD_POSTS_REQUEST', 'LOAD_POSTS_SUCCESS', 'LOAD_POSTS_FAILURE'],
    // Check the cache (optional):
    shouldCallAPI: (state) => !state.posts[userId],
    // Perform the fetching:
    callAPI: () => fetch(`http://myapi.com/users/${userId}/posts`),
    // Arguments to inject in begin/end actions
    payload: { userId }
  }
}

毫无疑问,现在的代码就简化了很多。

不过这个api middleware比较简略一些,像callAPI每次都要完整的重新写就,这样固然具备了最大的灵活性,但却让代码显得累赘。

redux项目的examples目录中有一个叫做real-world的例子,里边有一个实现的更加完善的api middleware,感兴趣的话,可以找来看看。

当然,real-world中那个api middleware并不能适用于任何情况,比如如果想在请求时带上token,发送POST请求,就需要对其进行一定量的改造,不过在搞清楚它的原理之后这些事情还是比较容易的。

Luo Gang

Read more posts by this author.