咱們知道 react-redux的 connect是一個高階組件,它將組件包裝以後拿到一個可以在組件中直接獲取 context 的 state 的組件,而且用dispatch監聽每個action:react
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
class Connect extends Component {
...
let dispatchProps = mapDispatchToProps
? mapDispatchToProps(store.dispatch, this.props)
: {} // 用來 dispatch 的時候獲取 store 的 dispatch
...
render() {
return <WrappedComponent {...this.state.allProps}/>
}
}
return Connect;
}
複製代碼
若是要加強dispatch,咱們能夠對其進行重構,直接改寫 store 實例中的 dispatch:git
let store = createStore(rootReducer);
let dispatch = store.dispatch//拿到dispatch
store.dispatch = function (action) {//對dispatch進行重構
console.log('舊狀態',store.getState())//1.打印就狀態
dispatch(action)//2.在執行以前的action
console.log('新狀態',store.getState())//3.打印新狀態
}
複製代碼
以上的代碼加強了dispatch方法,使執行順序變成了action->log->reducer->log
,執行結果以下:github
redux 提供了相似後端 Express 的中間件概念,本質的目的是提供第三方插件的模式,自定義攔截 action -> reducer
的過程。變爲 action -> middlewares -> reducer
。這種機制可讓咱們改變數據流,實現如異步 action ,action 過濾,日誌輸出,異常報告等功能。redux
官方說明以下:使用中間件擴展加強Redux store上的
dispath
方法。由於中間件多是異步的,因此這應該是定義在組合鏈
中存儲加強器。後端
export default function applyMiddleware(...middlewares) {//[middleware1,middleware2]
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))//將middleware放入鏈式數組
dispatch = compose(...chain)(store.dispatch)//依次執行
return {// 將加強過的dispatch返回
...store,
dispatch
}
}
}
複製代碼
applyMiddleware的使用方法,官方文檔中給出兩種
用法:promise
const store = createStore(reducer, preloadedState, applyMiddleware(...))
const store = createStore(reducer, applyMiddleware(...))
複製代碼
第二個參數初始化state不是必傳的,源碼中的createStore方法對參數進行了處理bash
export default function createStore(reducer, preloadedState, enhancer) {
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)
}
......
}
複製代碼
在createStore方法中判斷參數並相應替換,最後createStore代碼執行返回的是一個enhancer函數嵌套調用方法,也就是:異步
const store = applyMiddleware(...)(createStore)(reducer,preloadedState)
複製代碼
如圖所示:嵌套函數分別傳入的createStore和reducer,建立了store
,並定義了dispatch方法,並組合成obj傳給了logger函數
logger中間件函數接受兩個參數 dispatch getState
(獲取狀態 派發動做) 並返回一個新的參數 next
,造成了一個dispatch加強函數。小白我對於這個一長串的return理解成以下:
let logger1 = function({dispatch,getState}) {
store.dispatch = function(action){
console.log('舊狀態1',getState())
next(action)
console.log('新狀態1',getState())
}
}
複製代碼
這已經跟文章開頭手動加強store.dispatch的函數十分相近了,主要區別在於next方法。
middleware 經過 next(action) 一層一層處理和傳遞 action
,直到 redux 原生的 dispatch`,這時next爲客戶端調用的dispatch方法,action爲方法傳入的actionType:{type:xxx,payload:xxx} 咳,代碼要優雅,小白我理解了就要按照官方的來,正確的middleWare同樣定義格式以下:
let logger = ({dispatch,getState}) => next => action =>{
console.log('舊狀態1',getState())
next(action)//dispatch(action)
console.log('新狀態1',getState())
}
複製代碼
中間件的實現和洋蔥模型很像,先觸發logger第一層,再觸發dispatch事件,最後再從logger函數出來。
實現多箇中間件前後調用的關鍵是compose函數
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製代碼
源碼很精簡,理解有點複雜。實際上是使用reduce不斷將最右
先調動函數的結果返回給後調用的函數。
舉個栗子,
function add1(str){
return str+1;
}
function add2(str){
return str+2;
}
function add3(str){
return str+3;
}
let add = compose(add3,add2,add1);
let r = add("啊哈哈哈")//啊哈哈哈123
複製代碼
在這段代碼中,compose函數執行順序爲add1->add2->add3,並將結果做爲參數
傳給下一個函數。
在redux中當新 dispatch 執行時,[f1, f2, ... , fx, ..., fn],從右到左依次執行。
dispatch = f1(f2(f3(store.dispatch))))
複製代碼
如圖所示,從右至左執行logger2,logger1。logger2返回的代碼做爲參數傳給logger1的next參數。按照圖上1->2(執行下一個middleware)->3->4(觸發redux 原生的 dispatch方法)->5->6 完成
鏈式middleware流程圖以下
不少時候,咱們須要異步操做。用戶觸發第一個dispatch事件的action,須要發送第二個action。或者根據返回的根據發送第二個處理請求。
解決異步操做的方法:
(1)redux函數的參數是dispatch和getState,把返回的obj改爲返回一個異步函數。
(2)異步操做結束以後,再發出一個 Action。
function incrementAsync() {
return dispatch => {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
}, 1000);
};
}
複製代碼
這樣子能理想得解決異步操做,而store.dispatch方法正常狀況下,參數只能是對象,不能是函數。
這個時候能夠引入redux-thunk
Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
redux-thunk實現源碼:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
複製代碼
redux-thunk 作的事情就是判斷 action 類型是不是函數,如果,則執行 action,若不是,則繼續傳遞 action 到下個 middleware。
運用方法:
let store = createStore(
rootReducer,
applyMiddleware(thunk,logger1,logger2)
)
複製代碼
如圖所示,當store.dispatch運行到thunk中間件,發現返回的是一個function,則執行返回的函數,並返回,從新派發dispatch
。
所以使用redux-thunk,改造store.dispatch。能夠實現異步方法
還有如 redux-promise redux-saga也能夠解決異步的問題