先熟悉两个关键代码:
applyMiddleware:
/* v3.5.2*/
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducers, initialState, enhancer) => {
const store = createStore(reducers, initialState, enhancer)
const dispatch = store.dispatch
const chain = []
const middleWareAPI = {
getState: store.getState,
dispatch: action => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch) // store.dispatch or dispatch both work
// compose will do following thing:
/*
* a, b, c ==> a(b(c())), indeed, it is just a reduce and store.dispatch will be an initial value
*/
return {
...store,
dispatch
}
}
}
redux-thunk:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) =>
(next) =>
(action) => {
// This gets called for every action you dispatch.
// If it's a function, call it.
if (typeof action === "function") {
return action(dispatch, getState, extraArgument)
}
// Otherwise, just continue processing this action as usual
return next(action)
}
}
const thunk = createThunkMiddleware()
thunk.withExtraArgument = createThunkMiddleware
export default thunk
applyMiddleware 的主要作用是对 dispatch 进行改造,那 thunk 对它做了什么呢?
thunk:
- 如果 action 是函数,就执行函数,将 dispatch 传入。dispatch 来自于 applyMiddleware 的传参 --> middlewareAPI
- 否则,进入下一个(next) middleware
next 表示 applyMiddleware 中的下一个 middleware。其中最后一个 middleware 就是 store.dispatch。如果 thunk 是唯一的中间件,那么作为普通对象的 action dispatch,就执行的是 next(action),也就是 redux 本来的 dispatch(action)
redux-thunk 使得 action 可以为函数,这样有什么好处呢?好处在对于异步的处理。
首先,action 是一个普通对象:
{
type: "USER_LOGGED_IN",
username: "dave"
}
其次,有 action creators,action 创造者,就是一个函数,方便用来生成 action:
function userLoggedIn() {
return {
type: 'USER_LOGGED_IN',
username: 'dave'
};
}
dispatch(userLoggedIn())
为了处理异步请求,我们希望:
function userLogOut() {
return function(dispatch, getState) { // 此处
return axios.post('/logout').then(function() {
// pretend we declared an action creator
// called 'userLoggedOut', and now we can dispatch it
dispatch(userLoggedOut())
})
}
}
dispatch(userLogOut())
相比于 action creators,我们不是返回一个对象,而是返回一个函数。经过 thunk 的处理,dispatch 时,此处
的函数就会被执行,这样异步请求之后的 dispatch 也会得到执行。
20220805 更新
本来昨天只是看了 dave 那篇博客,对比 applyMiddleware 理解了 redux-thunk。 今天又看了 redux-thunk 的 GitHub 页面,原来 redux-thunk 项目已经介绍的很全面很好了,唯一的不足是没有对 middleware 的介绍,可以参考文章上面的源码。
当你接触到 thunk 的时候,可能就在不断接收它的用法,概念(一个很难理解的词),但是容易忽略的一点是为什么 redux 需要 redux-thunk。所以这一点要先记住。
// But what do you do when you need to start an asynchronous action,
// such as an API call, or a router transition?
dispatch 的参数最终是一个对象,不管是直接传入一个对象还是传入一个 action creator 函数生成一个对象。如果需要进行异步操作呢?比如获取文章列表。redux 如何触发一个动作?
一个 action creator 是一个函数,这个函数可以返回一个 thunk。 当我们 dispatch 这个函数的时候,就像 dispatch 一个普通的 action creator 函数。
综上,thunk 其实是利用了 JavaScript 函数的强大功能。
然而,异步也不一定非要 dispatch 触发,比如 Dan 回答 Stack Overflow: Why do we need middleware for async flow in Redux? 时所做的:
let nextNotificationId = 0
export function showNotificationWithTimeout(dispatch, text) {
const id = nextNotificationId++
dispatch(showNotification(id, text))
setTimeout(() => {
dispatch(hideNotification(id))
}, 5000)
}
// component.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged in.')
// otherComponent.js
showNotificationWithTimeout(this.props.dispatch, 'You just logged out.')
定义一个函数,函数中有异步有 dispatch。对于简单的应用,这样就够了。当然它有很多缺点,所以才需要 redux-thunk.