[譯]「thunk」到底是個什麼東西
原文地址What the heck is a 'thunk'?
問:你瞭解thunk嗎?
答:你第一次聽說redux-thunk
時頭皮發麻的聲音。
抱歉,這樣的回答實在是有點糟糕。
但是,講真的:如果你之前沒有接觸過,Redux Thunk 絕對是個令人迷惑的東西。我覺得大部分原因在於thunk
這個單詞吧。那麼讓我們先把這個單詞的意思搞明白吧。
thunk,名詞
thunk 是函式(function)的另一種表達方式。但它並不僅僅是傳統的函式,而是由其他函式返回的一種特殊且不常見的函式的別稱。比如下面這個:
function wrapper_function(){ // 這個函式就稱之為 thunk,它的功能是將任務延遲執行 return function thunk(){ // 可以是具名函式,也可以匿名 console.log('do stuff now') } } 複製程式碼
其實你已經瞭解過或用過這種模式。只不過你不知道他就叫thunk
罷了。如果你想列印do stuff now
,只需要執行兩次wrapper_function
就可以了,即wrapper_function()()
。
redux-thunk
那麼,這種形式的函式又是如何應用到Redux中去的呢?
如果你對Redux熟悉的話,你會知道其中有幾個重要的概念:actions
、action creators
、reducers
和middleware
。
actions
即普通的物件。就Redux而言,開箱即用的actions
必須是普通的物件,而且其必須含有一個type
屬性。除了上述要求,你可以在這個物件中描述任何你需要執行的action
。actions
形式如下:
// 1. 普通物件 // 2. 有一個type屬性 // 3. 任何其他你需要的 { type: "USER_LOGGED_IN", username: "dave" } 複製程式碼
由於一直重複的去寫這些物件很煩人,於是 Redux 就有了action-creators
的概念。
function userLoggedIn() { return { type: 'USER_LOGGED_IN', username: 'dave' }; } 複製程式碼
這個雖然看起來跟前面的action
是一樣的,但現在方便的是你可以通過呼叫userLoggedIn
函式來生成action
了。這樣,就對其進行了一次抽象。
現在你可以通過呼叫函式來建立actions
返回物件了,再也不用你去手敲了。這時如果你需要在你的專案中 dispatch 多次相同的action
,action creators
就能幫你省很多力氣了。
Actions太枯燥了
現在你有沒有發現一個有趣的事情,Redux所謂的actions
實際上啥都沒幹。它們就是物件而已,普通、簡單又沒什麼用武之地。
那麼如果你真的可以讓它們做點什麼,那不是很酷嗎?比方說,呼叫一個API,或是觸發其他操作?
由於reducers
應該是純函式(不改變任何作用域外的東西),所以我們並不能在一個 reducer 內部呼叫任何API或者是 dispatch 一個action
。
如果你想要讓一個action
去做點兒什麼,那麼你的程式碼應該包含在一個函式內部。這個函式(也即 thunk)是一系列將來才會完成的操作。
要是action creators
可以完成這樣的功能那就太棒了。它會返回我們需要的一系列將來會被執行的動作,而不是簡單的物件。比如下面這樣:
function getUser() { return function() { return axios.get('/current_user'); }; } 複製程式碼
現在好了,redux-thunk
確實就是幹這個的。它就是個中介軟體,去監控傳入系統中的每一個action
,如果是個函式的話,那麼它就會呼叫那個函式。這就是redux-thunk
的職責。
在上面的示例中,我唯一漏掉的一點就是Redux會傳遞兩個引數給thunk函式:dispatch
- 如果需要的話可以 dispatch 新的action
;getState
- 用於訪問當前 state。
function logOutUser() { return function(dispatch, getState) { return axios.post('/logout').then(function() { // 假設我們宣告過一個 action creator // 叫做 'userLoggedOut', 現在我們可以 dispatch 它了 dispatch(userLoggedOut()); }); }; } 複製程式碼
再補充一點:getState
還可用於決定是獲取新資料還是返回快取結果,這取決於當前 state。
這就是redux-thunk
做的事情了。
著實是個很小的庫了
redux-thunk
庫的完整原始碼都在這了:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { // 在這兒呼叫所有你 dispatch 的 action // 如果是個函式的話,直接呼叫 if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } // 否則,繼續處理該action return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk; 複製程式碼
在你專案中安裝了redux-thunk
後,你 dispatch 的每一個 action 都會經過這幾行程式碼的處理。它呼叫actions
為函式的actions
(不去管它返回什麼),如果actions
不是函式,就將其傳給下一個中介軟體或者Redux本身(也就是next(action)
這行程式碼所做的工作)。
在你的專案中使用 redux-thunk
如果你的專案中已經配置好了 Redux,那麼新增redux-thunk
只需兩步:
首先,安裝redux-thunk
:
npm install --save redux-thunk 複製程式碼
然後,不論你的Redux配置程式碼在哪兒,你只需引入redux-thunk
後把該中介軟體插入到Redux當中去:
// 你需要引入 appleMiddleware import { createStore, applyMiddleware } from 'redux'; // 引入 thunk 中介軟體 import thunk from 'redux-thunk'; // 引入現存的根reducer路徑 // 改變該路徑以適應你的配置 import rootReducer from './reducers/index'; // createStore 的最後一個引數為「增強store」, // 這裡我們基於 thunk 中介軟體,使用applyMiddleware來建立該「增強store」 const store = createStore( rootReducer, applyMiddleware(thunk) ); 複製程式碼
只需確保你正確的呼叫applyMiddleware
時傳入了 thunk,否則它就不能起作用了。