最近剛剛完成了畢業答辯,個人畢設內容是基於React系列技術棧開發的一個相似Instagram的Web App,戳此看看。開發完後,我驚奇的發現:咦,以前就據說有個叫作redux-thunk的東西,我怎麼沒用到?業務場景太簡單了?因而大概研究了下。。git
關於redux-thunk的基本介紹,也許你能夠先看看stackoverflow上面的介紹。我的理解:redux-thunk改寫了dispatch API,使其具有接受一個函數做爲參數的能力,從而達到middleware的效果,即在redux的dispatch action => reducer => store這個流程中,在action 被髮起以後,到達 reducer 以前的擴展點,加入相關操做,好比發生請求、log信息等。github
以我本次畢設項目中,在redux流程中加入異步請求爲例,爲動態點贊功能部分代碼實現:redux
//from action.js
const LIKE = (id) => ({
type: "LIKE",
id:id
})
reqLike({id:id}).then(res =>{ dispatch(LIKE(id))})
複製代碼
能夠看到,我在請求之後的回調函數中dispatch action去同步redux store中的狀態。app
//from action.js
const LIKE = (id) => {
return function (dispatch,getState) {
reqLike({id:id}).then(res =>{
dispatch({
type: "LIKE",
id:id
})
})
}
}
dispatch(LIKE(id))
複製代碼
改變之後,從功能層面上來講,二者並沒有差異,均可以知足業務場景需求。但除此以外咱們能夠發現:異步
瞭解了redux-thunk的基本概念以及應用後,咱們一塊兒看看源碼加深下理解吧,源碼十分精巧。ide
首先看到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.ui
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.spa
//傳入到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的源碼了!code
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外,接受一個函數做爲參數。