您好,登錄后才能下訂單哦!
前言
剛來公司的時候,對react項目中的thunk中間件的作用一直不太了解,最近有時間決定好好研究一下。鑒于本人初次寫博客,并已假設讀者已掌握redux的一些基本用法;如有錯誤,還望指出。不勝感激!
首先簡單回顧一下redux工作流程
圖畫的不太好,見諒;
對于reactUI組件來說,數據的來源無外乎兩種,一種是用戶主動觸發的動作,例如點擊事件、提交表單,輸入操作;另一種是組件主動的數據更新,如獲取頁面初始數據,子組件接受父組件的props變化而進行更新視圖操作;
如圖所示,無論那種對于數據的操作,對于view都會派發出一個action
狀態的更新
正如我們所知,在redux里,每次更新后的Store都會對應著一個新的view,而Store里面數據的更新依賴action的觸發————Store.dispatch(action)會自執行初始化中createStore中注入的reducers,從而計算出新的狀態。
import { createStore } from 'redux' //reducer 計算狀態的純函數 //initialState 初始化數據 //enhancers中間件 createStore(reducers, initialState, enhancers)
action的使用和插件的擴展
對于組件的輸入操作(如點擊事件),可以將store.dispatch(action)綁定到組件
const store = createStore(reducer); const TodoList = ({ state, someActionCreator }) => ( <ul> {state.map(someState => <Todo key={someState.someData} onClick={() => store.dispatch(someActionCreator(state.someData))} /> </ul> )
或者通過connect方法,從組件的props中拿到dispatch方法,發出一個action
// 將state注入到組件的props里 // 注意,這里的state指的是redux管理的數據,每一個view的狀態對應著 // 唯一的state; // state的集合就是redux管理的store const mapStateToProps = store => ({ state: store.state }) // 將action注入到組件的props 里 const mapDispatchToProps = dispatch => ({ actions: state => dispatch(actionCreators(state)) }) export default connect( mapStateToProps, mapDispatchToProps )(TodoList)
然后組件綁定事件就可以改成這樣 ,( actionCreators用于生成action, 參考官方鏈接 https://redux.js.org/basics/actions)
const TodoList = ({ state, actions }) => ( `<ul> {state.map(someState => <Todo key={someState.someData} onClick={() => actions(someState.someData)} /> </ul>` )
那么問題來了,dispatch是同步執行reducers生成新狀態的,對于頁面的操作沒有問題;但是如果點擊事件是請求了某個結果,需要等待結果響應后再更新視圖呢?應該如何處理?
因而redux引入了thunk中間件,對action進行了擴展
##thunk中間件解決了什么問題?
引入thunk插件后,我們可以在actionCreators內部編寫邏輯,處理請求結果。而不只是單純的返回一個action對象。
//未引入前的寫法 let nextTodoId = 0 export const addTodo = text => ({ type: 'ADD_TODO', id: nextTodoId++, text }) //引入thunk后 let nextTodoId = 0 export const addTodo = text => ({ return async dispatch => { //dosomething, request await request() dispatch({ type: 'ADD_TODO', id: nextTodoId++, text }) } })
thunk中間件的使用方法
import { applyMiddleware, createStore } from 'redux'; import thunk from 'redux-thunk'; const store = createStore( reducer, applyMiddleware(thunk) );
createStore其實可以接受三個參數,第二個參數preloadedState一般作為整個應用的初始化數據,如果傳入了這個參數,applyMiddleware就會被當做第三個參數處理
const store = createStore( reducer, initialState, applyMiddleware(thunk) );
中間件都要放到applyMiddleware里,如果要添加中間件,可以依次添加,但是要遵循文檔定義的順序
const store = createStore( reducer, initialState, applyMiddleware(thunk,middleware1, middleware2) );
源碼解讀
也許你會奇怪,為什么使用的時候要按照上面的寫法,那我們就一起看下方法的實現
首先是createStore的參數順序
function createStore(reducer, preloadedState, enhancer) { var _ref2; if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState; preloadedState = undefined; } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.'); } return enhancer(createStore)(reducer, preloadedState); } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.'); }
第一個判斷已經告訴了我們答案,參數的類型檢驗結果決定了順序
applyMiddleware是干什么用的 function applyMiddleware() { for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } return function (createStore) { return function () { for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } var store = createStore.apply(undefined, args); var _dispatch = function dispatch() { throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.'); }; var middlewareAPI = { getState: store.getState, dispatch: function dispatch() { return _dispatch.apply(undefined, arguments); } }; var chain = middlewares.map(function (middleware) { return middleware(middlewareAPI); }); _dispatch = compose.apply(undefined, chain)(store.dispatch); return _extends({}, store, { dispatch: _dispatch }); }; }; }
代碼不多,而且非常清晰:
1、applyMiddleware顧名思義,用于調用各種中間件;
2、applyMiddleware執行后,將所有入參中間件存入一個數組,并且返回一個閉包(閉包的概念不做累述)
3、閉包接受一個createStore作為入參并且執行后返回下一個閉包,createStore這個入參有沒有很眼熟,沒錯,就是redux的createStore。
返回結果
返回將所有中間件串聯存入的dispatch,執行時從右向左執行,第一次的執行結果會返回給一下個,依次類推。
如何實現每個中間件串聯執行
_dispatch = compose.apply(undefined, chain),使用了一個compose函數,調用之后就可以將所有中間件串聯起來,那么compose又是如何實現的呢?
精華所在
function compose() { for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) { funcs[_key] = arguments[_key]; } if (funcs.length === 0) { return function (arg) { return arg; }; } if (funcs.length === 1) { return funcs[0]; } return funcs.reduce(function (a, b) { return function () { return a(b.apply(undefined, arguments)); }; }); }
個人認為這個compose函數是整個redux中非常亮眼的部分,短短幾行代碼,就完成了一個核心功能的擴展,是責任鏈設計模式的經典體現。
ALSO 我們也可以使用這個compose方法對applyMiddleware進行擴展
let devtools = () => noop => { console.log(noop); return noop; //createStore }; const enhancers = [ applyMiddleware(...middleware), devtools() ]; createStore(reducers, initialState, compose(...enhancers));
然后回來,我們就明白了createStore中的設計
//如果存在中間件參數,那么將會得到一個經過改裝的dispatch // return _extends({}, store, {dispatch: _dispatch}); if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.'); } return enhancer(createStore)(reducer, preloadedState); }
dispatch經過了怎樣的改裝
如上已經說過,compose會將傳入的函數數組從右向左串聯執行
compose.apply(undefined, chain)(store.dispatch);
thunk一定會接受上一個中間件的執行結果繼續執行,然后最終在createState里返回一個改造好的dispatch, 接下來我只要看下thunk是怎樣實現的,就了解了整個中間件使用的原理:
function createThunkMiddleware(extraArgument) { return function (_ref) { var dispatch = _ref.dispatch, getState = _ref.getState; return function (next) { //最終的dispatch //next就是接收的store.dispatch參數,為上一個中間件改造過的dispatch return function (action) { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } return next(action); }; }; }; } var thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
代碼同樣精煉,改造后的dispatch入參接受的數據類型:
1、非function,不處理,將action 傳給下一個中間件,最終都會根據傳入的action計算相應的reducers(開頭說的自執行)————store.dispatch(action)
2、function類型的action, 自動觸發函數,并且將store.dispatch傳入
總結
再結合開始介紹的thunk用法,我們就明白了thunk的原理,可以在actionCreators里通過返回一個函數,然后就可以在函數里編寫某些異步操作了,待異步操作結束,最后通過傳入的store.dispatch,發出action通知給Store要進行狀態更新。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。