喵了個咪!redux middleware竟然如此簡單!

Redux解決了react中出現的多交互、多數據源問題,可是若是有異步操做,或者要對操做進行攔截或者執行後回調就比較麻煩。因而咱們須要Redux 中間件。

1、手動加強store.dispatch

咱們知道 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

2、使用redux的applyMiddleware方法加強store.dispatch

redux 提供了相似後端 Express 的中間件概念,本質的目的是提供第三方插件的模式,自定義攔截 action -> reducer 的過程。變爲 action -> middlewares -> reducer 。這種機制可讓咱們改變數據流,實現如異步 action ,action 過濾,日誌輸出,異常報告等功能。redux

官方說明以下:使用中間件擴展加強Redux store上的dispath 方法。由於中間件多是異步的,因此這應該是定義在組合鏈中存儲加強器。後端

redux applyMiddleware方法源碼數組

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

3、middleware如何工做

redux createStore源碼app

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函數出來。

4、compose實現鏈式調用

實現多箇中間件前後調用的關鍵是compose函數

redux 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流程圖以下

5、異步操做

不少時候,咱們須要異步操做。用戶觸發第一個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也能夠解決異步的問題

相關文章
相關標籤/搜索