使用异步action
redux除了可以执行同步action外,还可以执行异步action.
标准的做法是使用 Redux Thunk 中间件。要引入 redux-thunk 这个专门的库才能使用。我们 后面 会介绍 middleware 大体上是如何工作的;目前,你只需要知道一个要点:通过使用指定的 middleware,action 创建函数除了返回 action 对象外还可以返回函数。这时,这个 action 创建函数就成为了 thunk。 不是只有返回了异步函数才能成为chunk 。
当 action 创建函数返回函数时,这个函数会被 Redux Thunk middleware 执行。这个函数并不需要保持纯净;它还可以带有副作用,包括执行异步 API 请求。这个函数还可以 dispatch action,就像 dispatch 前面定义的同步 action 一样。
我们仍可以在 actions.js 里定义这些特殊的 thunk action 创建函数。
export function getCurrentTime() {return function fetchCurrentTime(dispatch) {dispatch(pending());return fetch("http://127.0.0.1:8080/api/common/currentTime").then(response => response.json(),error => console.log("error", error)).then(res => {if(res.msg === "ok") {dispatch(setCurrentTime({currentTime2: res.data.currentTime,status: "success",msg: "ok",}))} else {dispatch(fail())}})}}
在dispatch中使用中间件需要引入Redux Thunk middleware:
import thunk from "redux-thunk";const { createStore, applyMiddleware } = require("redux");const { timeTool } = require("./reducer");let store = createStore(timeTool, applyMiddleware(thunk));export default store;
再次dispatch
thunk 的一个优点是它的结果可以再次被 dispatch:
import fetch from 'cross-fetch'export const REQUEST_POSTS = 'REQUEST_POSTS'function requestPosts(subreddit) {return {type: REQUEST_POSTS,subreddit}}export const RECEIVE_POSTS = 'RECEIVE_POSTS'function receivePosts(subreddit, json) {return {type: RECEIVE_POSTS,subreddit,posts: json.data.children.map(child => child.data),receivedAt: Date.now()}}export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'export function invalidateSubreddit(subreddit) {return {type: INVALIDATE_SUBREDDIT,subreddit}}function fetchPosts(subreddit) {return dispatch => {dispatch(requestPosts(subreddit))return fetch(`http://www.reddit.com/r/${subreddit}.json`).then(response => response.json()).then(json => dispatch(receivePosts(subreddit, json)))}}function shouldFetchPosts(state, subreddit) {const posts = state.postsBySubreddit[subreddit]if (!posts) {return true} else if (posts.isFetching) {return false} else {return posts.didInvalidate}}export function fetchPostsIfNeeded(subreddit) {// 注意这个函数也接收了 getState() 方法// 它让你选择接下来 dispatch 什么。// 当缓存的值是可用时,// 减少网络请求很有用。return (dispatch, getState) => {if (shouldFetchPosts(getState(), subreddit)) {// 在 thunk 里 dispatch 另一个 thunk!return dispatch(fetchPosts(subreddit))} else {// 告诉调用代码不需要再等待。return Promise.resolve()}}}
thunk middleware并不是处理异步action的唯一方式:
- 你可以使用 redux-promise 或者 redux-promise-middleware 来 dispatch Promise 来替代函数。
- 你可以使用 redux-observable 来 dispatch Observable。
- 你可以使用 redux-saga 中间件来创建更加复杂的异步 action。
- 你可以使用 redux-pack 中间件 dispatch 基于 Promise 的异步 Action。
- 你甚至可以写一个自定义的 middleware 来描述 API 请求,就像这个 真实场景的案例 中的做法一样。
你也可以先尝试一些不同做法,选择喜欢的,并使用下去,不论有没有使用到 middleware 都行。
