Understanding redux-thunk

Louis
6 min readMar 11, 2020

Redux-thunk 原始碼

短短14行到底做了什麼呢XD?

開始之前我們先看一下 redux 的 applyMiddleware 做了什麼,這樣回過頭來看redux-thunk的原始碼才更清楚。

Redux API: applyMiddleware() api

Each middleware receives Store's dispatch and getState functions as named arguments, and returns a function. The last middleware in the chain will receive the real store’s dispatch method as the next parameter, thus ending the chain. So, the middleware signature is ({ getState, dispatch }) => next => action

每個 middleware 會接收 StoredispatchgetState functions 作為具名參數,並回傳一個 function。最後的 middleware 會接收到 store 的 dispatch 作為 next 傳入,最後結束。

這樣回頭看 redux-thunk 就相當簡單了,thunk 把宣告成 function 的 action 攔截下來在包了一層,可以想成是 closure 的應用,繼續往下傳遞,由最後我們所宣告的 function action 來執行內容,那正常的 action (我相信應該會是 object),就會由 next 直接執行 (即 dispatch )。

如果有人還是覺得一定要看一下 source code呢 …

那我們就來看R,我截去ts的部分,這樣比較乾淨一點

function applyMiddleware(...middlewares) {  return (createStore) => (reducer, ...args) => {    
const store = createStore(reducer, ...args);
let dispatch = () => {
throw new Error('Apply Middleware Error');
}
const middlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
const chain = middlewares.map(
middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}

一個個來看看


let dispatch = () => {
throw new Error('Apply Middleware Error');
}

先設置如果後面dispatch 在 compose middlewares 只傳回自己時,throw error,就是 chain 為空的意思。

middlewareAPI

很 Ez,就是前面({ getState, dispatch }),要收到所準備的參數。

const chain = middlewares.map(
middleware => middleware(middlewareAPI));

每個middleware 都包裝 getState, dispatch 進入產生 middleware 陣列。

dispatch = compose(...chain)(store.dispatch);

compose 是個啥呢

function compose(...funcs) {  return funcs.reduce((a, b) => (...args) => a(b(...args)))}

假設有三個 middleware 好了

m1 = ({ getState, dispatch }) => (next) => (action) => {  console.log('m1');  return next(action);
}
m2 = ({ getState, dispatch }) => (next) => (action) => { console.log('m2'); return next(action);
}
m3 = ({ getState, dispatch }) => (next) => (action) => { console.log('m3'); return next(action);
}
還記得剛剛包裝過 getState, dispatch了嗎?m1Middleware = m1(middlewareAPI);m1Middleware = (next) => (action) => { console.log('m1'); ..., return next;
}
m2Middleware = (next) => (action) => { console.log('m2'); ..., return next;
}
m3Middleware = (next) => (action) => { console.log('m3');

...,
return next;
}

就會變成 m1Middleware(m2Middleware(m3Middleware(store.dispatch)))

log
m3 -> m2 -> m1
const m1 = (next) => (action) => {console.log("m1");return next(action);}const m2 = (next) => (action) => {console.log("m2");return next(action);}const fakeDispatch = (action) => {console.log("Final Dispatch this action", action);}const fakeActionYouInput = {type: "fakeType"}m1(m2(fakeDispatch))(fakeActionYouInput);m1
m2
Final Dispatch this action { type: 'fakeType' }

本來以為可以更簡短,結果越寫越多,希望日後回來看不會一頭霧水。

--

--