使用异步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 都行。