前言
最近刚刚完成了毕业答辩,我的毕设内容是基于React系列技术栈开发的一个类似Instagram的Web App,戳此看看。开发完后,我惊奇的发现:咦,之前就听说有个叫做redux-thunk的东西,我怎么没用到?业务场景太简单了?于是大概研究了下。。
概念的基本介绍
关于redux-thunk的基本介绍,也许你可以先看看stackoverflow上面的介绍。个人理解:redux-thunk改写了dispatch API,使其具备接受一个函数作为参数的能力,从而达到middleware的效果,即在redux的dispatch action => reducer => store这个流程中,在action 被发起之后,到达 reducer 之前的扩展点,加入相关操作,比如发生请求、log信息等。
实际使用
以我本次毕设项目中,在redux流程中加入异步请求为例,为动态点赞功能部分代码实现:
- 本来我是这样写的
//from action.js
const LIKE = (id) => ({
type: "LIKE",
id:id
})
reqLike({id:id}).then(res =>{ dispatch(LIKE(id))})
复制代码
可以看到,我在请求以后的回调函数中dispatch action去同步redux store中的状态。
- 加入redux-thunk之后我是这样写的:
//from action.js
const LIKE = (id) => {
return function (dispatch,getState) {
reqLike({id:id}).then(res =>{
dispatch({
type: "LIKE",
id:id
})
})
}
}
dispatch(LIKE(id))
复制代码
改变以后,从功能层面上来说,两者并无差别,都可以满足业务场景需求。但除此之外我们可以发现:
- 1.dispatch接受的参数由一个PlainObject变为一个函数
- 2.我们把请求的异步操作从dispatch action这个redux流程外塞到的流程里,这看起来将异步操作内聚到这个流程中,无论是从逻辑上理解(这很middleware!)还是项目代码开发维护(区分异步与同步状态管理流程进行维护管理)上都是很大的改进
- 3.如果项目中有多处需要实现点赞功能,我们可以节省很多冗余代码,不用到处在dispatch外层套上reqLike(id).then(...)
源码解析
了解了redux-thunk的基本概念以及应用后,我们一起看看源码加深下理解吧,源码十分精巧。
首先看到redux源码中applyMiddleware的部分,我们将thunk作为参数传入之后,直接返回了一个函数,这个函数作为enhancer传入redux源码中的createStore函数中。
export default function applyMiddleware(...middlewares) {
//这个返回函数就是enhancer
return (createStore) => (reducer, preloadedState, enhancer) => {
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
在redux源码中的createStore函数中,enhancer被执行,传入参数createStore,又紧接着执行其返回的函数,传入reducer和preloadedState.
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
复制代码
接下来,我们进入applyMiddleware和thunk的关键部分,上面applyMiddleware接受的最初的(...middlewares)参数其实就是thunk,thunk会被执行,并且传入参数getState和dispatch.
//传入到thunk的参数
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
//在map中执行thunk
chain = middlewares.map(middleware =>middleware(middlewareAPI))
//重新改写dispatch
dispatch = compose(...chain)(store.dispatch)
复制代码
那么上面的chain是什么呢,我们终于可以去看redux-thunk的源码了!
function createThunkMiddleware(extraArgument) {
return function (_ref) {
var dispatch = _ref.dispatch,
getState = _ref.getState;
//这里返回的函数就是chain
return function (next) {
//这里返回的函数就是改写的dispatch
return function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
};
};
}
var thunk = createThunkMiddleware();
复制代码
从源码我们可以看出,chain就是以next作为形参的匿名函数,至于compose只是不断传递每个函数的返回值给下一个执行函数,然后依次去执行它所有传入的函数而已,它源码中的注释说的很清楚:For example, compose(f, g, h) is identical to doing (...args) => f(g(h(...args))).
我们这里的chain只是一个函数而已,所以很简单,就是执行chain,并且传入store.dispatch作为next就行。
接下来,进入最后一步,改写了dispatch,最终变为:
function (action) {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
//next为之前传入的store.dispatch,即改写前的dispatch
return next(action);
};
复制代码
如果传入的参数是函数,则执行函数。否则还是跟之前一样dispatch(PlainObject).
总结
redux-thunk实现了相关异步流程内聚到redux的流程中,实现middleware的功能,也便于项目的开发与维护,避免冗余代码。而实现的方式便是改写redux中的dispatch API,使其可以除PlainObject外,接受一个函数作为参数。