Redux中間件用法詳解-redux-thunk/redux-logger/redux-devtools-extension

參考:https://blog.csdn.net/hsany330/article/details/105951197

 前面我們有說過,redux由三大核心組成部分:reducer、action、store。

其中,Reducer是一個純函數,只承擔計算 State 的功能,不合適承擔其他功能,也承擔不了,因爲理論上,純函數不能進行讀寫操作。

所以就有了redux中間件。

默認情況下,Redux 自身只能處理同步數據流。但是在實際項目開發中,狀態的更新、獲取,通常是使用異步操作來實現。

  • 問題:如何在 Redux 中進行異步操作呢?
  • 回答:通過 Redux 中間件機制來實現

1.redux中間件概念

  • 中間件,可以理解爲處理一個功能的中間環節
  • 中間件的優勢:可以串聯、組合,在一個項目中使用多箇中間件
  • Redux 中間件用來處理 狀態 更新,也就是在 狀態 更新的過程中,執行一系列的相應操作

dispatch一個action之後,到達reducer之前,進行一些額外的操作,就需要用到middleware。你可以利用 Redux middleware 來進行日誌記錄、創建崩潰報告、調用異步接口或者路由等等。
換言之,中間件都是對store.dispatch()的增強

2.中間件的觸發時機

  • Redux 中間件執行時機:在 dispatching action 和 到達 reducer 之間。
  • 沒有中間件:dispatch(action) => reducer

  • 使用中間件:dispatch(action) => 執行中間件代碼 => reducer

原理:封裝了 redux 自己的 dispatch 方法
沒有中間件:store.dispatch() 就是 Redux 庫自己提供的 dispatch 方法,用來發起狀態更新
使用中間件:store.dispatch() 就是中間件封裝處理後的 dispatch,但是,最終一定會調用 Redux 自己的 dispatch 方法發起狀態更新

redux的異步數據流動過程:

3.中間件的原理

Redux 中間件原理:創建一個函數,包裝 store.dispatch,使用新創建的函數作爲新的 dispatch

 

比如下圖,logger 就是一箇中間件,使用該中間件後 store.dispatch 就是包裝後的新 dispatch

 

中間件修改了 store.dispatch,在分發動作和到達 reducer 之間提供了擴展

 

redux 中間件採用了 洋蔥模型 來實現

  • 自己實現記錄日誌的 redux 中間件:

// 簡化寫法:
// store 表示:redux 的 store
// next 表示:下一個中間件,如果只使用一箇中間,那麼 next 就是 store.dispatch(redux 自己的 dispatch 函數)
// action 表示:要分發的動作
const logger = store => next => action => {
  console.log('prev state:', store.getState()) // 更新前的狀態
  // 記錄日誌代碼
  console.log('dispatching', action)
  // 如果只使用了一箇中間件:
  // 那麼,next 就表示原始的 dispatch
  // 也就是:logger中間件包裝了 store.dispatch
  let result = next(action)
  // 上面 next 代碼執行後,redux 狀態就已經更新了,所以,再 getState() 拿到的就是更新後的最新狀態值
  // 記錄日誌代碼
  console.log('next state', store.getState()) // 更新後的狀態
  return result
}
 
// 完整寫法:
const logger = store => {
  return next => {
    return action => {
      // 中間件代碼寫在這個位置:
    }
  }
}

 

4.中間件的用法

redux中間件需要在createStore()生成store時註冊:

import { applyMiddleware, createStore, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import reducer1 from '../reducer/reducer1';
import reducer2 from '../reducer/reducer2';
const reducers = combineReducers(reducer1);
const store = createStore(
  reducers, 
  applyMiddleware(thunk)
);
export default store;

 redux常見的中間件有redux-thunk,redux-logger,redux-saga等。

redux-devtools-extension中間件

目標:能夠使用chrome開發者工具調試跟蹤redux狀態

內容:

redux-devtools-exension 文檔:https://www.npmjs.com/package/redux-devtools-extension

先給 Chrome 瀏覽器安裝 redux 開發者工具,然後,就可以查看 Redux 狀態了

步驟:

1.安裝: yarn add redux-devtools-extension

2.從該中間件中導入 composeWithDevTools 函數

3.調用該函數,將 applyMiddleware() 作爲參數傳入

4.打開 Chrome 瀏覽器的 redux 開發者工具並使用

import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
 
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))
 
export default store

 

redux-thunk中間件

源碼解讀,原理([email protected]源碼:https://github.com/reduxjs/redux-thunk/blob/v2.3.0/src/index.js):

function createThunkMiddleware(extraArgument) {
  // Redux 中間件的寫法:const myMiddleware = store => next => action => { /* 此處寫 中間件 的代碼 */ }
  return ({ dispatch, getState }) => (next) => (action) => {
    // redux-thunk 的核心代碼:
    // 判斷 action 的類型是不是函數
    
    // 如果是函數,就調用該函數(action),並且傳入了 dispatch 和 getState
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    
    // 如果不是函數,就調用下一個中間件(next),將 action 傳遞過去
    // 如果沒有其他中間件,那麼,此處的 next 指的就是:Redux 自己的 dispatch 方法
    return next(action);
  };
}
 
// 所以,在使用了 redux-thunk 中間件以後,那麼,redux 就既可以處理 對象形式的 action 又可以處理 函數形式的 action 了
// 1 處理對象形式的 action
dispatch({ type: 'todos/clearAll' }) // 對應上面第 14 行代碼
 
// 2 處理函數型的 action
export const clearAllAsync = () => {
  return dispatch => {
    // 在此處,執行異步操作
    setTimeout(() => {
      // 異步操作完成後,如果想要修改 redux 中的狀態,就必須要
      // 分發一個 對象形式的 action(同步的 action)
      dispatch({ type: types.CLEAR_ALL })
    }, 1000)
  }
}
dispatch(clearAllAsync()) // 對應上面第 8、9 行代碼

 

目標:能夠使用redux-thunk中間件處理異步操作

內容:

redux-thunk 中間件可以處理函數形式的 action。因此,在函數形式的 action 中就可以執行異步操作

語法:

thunk action 是一個函數

函數包含兩個參數:1 dispatch     2 getState

// 函數形式的 action
const thunkAction = () => {
  return (dispatch, getState) => {}
}
 
// 解釋:
const thunkAction = () => {
  // 注意:此處返回的是一個函數,返回的函數有兩個參數:
    // 第一個參數:dispatch 函數,用來分發 action
  // 第二個參數:getState 函數,用來獲取 redux 狀態
  return (dispatch, getState) => {
    setTimeout(() => {
      // 執行異步操作
      // 在異步操作成功後,可以繼續分發對象形式的 action 來更新狀態
    }, 1000)
  }
}

使用 redux-thunk 中間件前後對比:

 

1.不使用 redux-thunk 中間件,action 只能是一個對象

// 1 普通 action 對象
{ type: 'counter/increment' }
dispatch({ type: 'counter/increment' })
 
// 2 action creator
const increment = payload => ({ type: 'counter/increment', payload })
dispatch(increment(2))

2.使用 redux-thunk 中間件後,action 既可以是對象,又可以是函數

// 1 對象:
// 使用 action creator 返回對象
const increment = payload => ({ type: 'counter/increment', payload })
// 分發同步 action
dispatch(increment(2))
 
// 2 函數:
// 使用 action creator 返回函數
const incrementAsync = () => {
  return (dispatch, getState) => {
    // ... 執行異步操作代碼
  }
}
// 分發異步 action
dispatch(incrementAsync())

步驟

  1. 安裝:yarn add redux-thunk
  2. 導入 redux-thunk
  3. 將 thunk 添加到 applyMiddleware 函數的參數(中間件列表)中
  4. 創建函數形式的 action,在函數中執行異步操作

核心代碼

store/index.js 中:

// 導入 thunk 中間件
import thunk from 'redux-thunk'
// 將 thunk 添加到中間件列表中
// 知道:如果中間件中使用 logger 中間件,logger 中間件應該出現在 applyMiddleware 的最後一個參數
const store = createStore(rootReducer, applyMiddleware(thunk, logger))

actions/index.js 中:

export const clearAllAsync = () => {
  return (dispatch) => {
    // 處理異步的代碼:1 秒後再清理已完成任務
    setTimeout(() => {
      dispatch(clearAll())
    }, 1000)
  }
}

 App.js 中:

import { clearTodoAsync } from '../store/actions/todos'
 
const TodoFooter = () => {
    return (
    // ...
      <button
      className="clear-completed"
      onClick={() => dispatch(clearTodoAsync())}
    >
      Clear completed
    </button>
  )
}

 

redux-logger中間件

目標:能夠使用redux-logger中間件記錄日誌

步驟:

1.安裝:yarn add redux-logger
2.導入 redux-logger 中間件
3.從 redux 中導入 applyMiddleware 函數
4.調用 applyMiddleware() 並傳入 logger 中間件作爲參數
5.將 applyMiddleware() 調用作爲 createStore 函數的第二個參數
然後,調用 store.dispatch() 查看 console 中 logger 中間件記錄的日誌信息

核心代碼:

store/index.js 中:

import { createStore, applyMiddleware } from 'redux'
import logger from 'redux-logger'
import rootReducer from './reducers'
 
const store = createStore(rootReducer, applyMiddleware(logger))

當然redux的中間件有很多,本文只講了其中的一部分。比如redux-saga也是解決redux中異步問題的中間件,redux-thunk是把異步操作放在action裏操作,而redux-saga的設計思想是把異步操作單獨拆分出來放在一個文件裏管理(面試常問)

其他中間件,像redux-promise、redux-saga等,此處篇幅有限,改日再講。此處先略過。有興趣的可參考:https://blog.csdn.net/yrqlyq/article/details/119243072

好了,吹水到此。編程使我快樂,快樂讓我吹水。歡迎留言互動互粉!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章