中间件的作用

  • 不使用中间件时,数据的修改流程:
    • 每当我们调用dispatch方法的时候,传递给dispatch的实参action通知对象,就会立即reducer函数接收,并执行reducer函数体内的代码,达到修改数据的目的。

中间件 middleware - 图1

  • 使用中间件时,数据的修改流程:

中间件 middleware - 图2
由上图我们可以知道,如果使用了中间件,则从我们发起dispatch开始,传入其中的action 对象,并不是直接交给reducer处理的,而是先经过了若干中间件链式处理过后,最后再由 reducer处理。所以我们可以变相的认为redux中间就是强化dispatch方法的一种机制

中间件的优势

在使用redux管理状态时候,可能会对触发action的过程,即dispatch有一些扩展的需求,redux提供了中间件机制来让开发者可以扩展dispatch。
比如,我们希望每次更新状态时候可以把更新之前的状态、action和更新之后的状态打印出来,这样可以方便地对应用进行调试和排查。
再比如,我们希望支持异步action,即dispatch可以传一个函数,函数中可以进行异步操作,异步更新状态(这个需求在调用接口获取数据的业务场景很常见)。

常用redux中间件

  • redux-logger:每次dispatch触发数据更新的时候,在控制台打印数据变化前后的值,该中间件只会在开发环境会用,生产环境不会用
  • redux-saga:用于管理redux异步操作的中间件
  • redux-thunk:用于管理redux异步操作的中间件

    redux-logger

    安装

    1. $ yarn add redux-logger

    配置

    在创建store的时候配置redux中间件
    1. import { createStore,applyMiddleware } from 'redux'
    2. import logger from 'redux-logger'
    3. import reducer from './reducers/index';
    4. const store = createStore(reducer,applyMiddleware(logger));
    配置好redux-logger之后,每次dispatch函数触发的时候就会打印日志了。

    redux-saga

    安装

    1. $ yarn add redux-saga

    使用

    redux-saga处理异步的流程大致为:
  1. dispatch派发一个异步任务的action
  2. action中的typeredux-saga监听了——>监听函数(里面包含异步任务)触发——>监听函数中的异步任务处理完之后,最终会再派发一个同步的action

    1. //sagas/index.js
    2. import {put,takeEvery,delay} from 'redux-saga/effects';
    3. function * test({payload}){
    4. yield delay(1000);//延时1s
    5. yield put({type:'add',payload})
    6. //派发同步action,这个action会给到reducer(这里的put就是dispatch)
    7. }
    8. export default function* index(){
    9. //asyncAdd这个type被redux-saga监听了,一旦被派发,则监听函数test会自动执行
    10. yield takeEvery("asyncAdd",test);
    11. }
    12. //如果项目功能复杂也可对sagas进行模块拆分 然后利用all方法进行合并
  3. reducer接受action,合并返回新的数据——>数据更新

    1. function reducer(state={num:0},action){
    2. switch(action.type){
    3. case 'add':
    4. return {
    5. ...state,
    6. num:state.num + aciton.payload
    7. }
    8. default:return state;
    9. }

    配置

    1. import {createStore,applyMiddleware} from 'redux';
    2. import createSgaMiddleware from 'redux-saga';
    3. import reducer from './reducers/index';
    4. import rootSaga from './sagas/index';
    5. const sagaMiddleware = createSgaMiddleware();
    6. export default createStore(reducer,applyMiddleware(sagaMiddleware));
    7. sagaMiddleware.run(rootSaga)

    redux-thunk

    让dispatch可以接受函数为参数(本来dispatch只能接受一个action对象为参数), 在函数内部可以写异步代码

    安装

    1. $ yarn add redux-thunk

    配置

    ```javascript import { legacy_createStore, combineReducers, applyMiddleware } from ‘redux’; import thunk from ‘redux-thunk’; import logger from ‘redux-logger’;

const reducer = combineReducers({ counter: countReducer, cart: cartReducer })

export const store = legacy_createStore(reducer, applyMiddleware(logger, thunk));

  1. <a name="NdgVf"></a>
  2. #### 使用
  3. 一旦遇到异步,则可以把`dispatch`接受的`action`对象变成函数,异步任务就放到函数里面去处理。不过,为了定制参数,我们一般还要在外面再套一层函数。
  4. ```javascript
  5. import React from 'react'
  6. import { useSelector, useDispatch } from 'react-redux';
  7. import { asynAddA } from '../../../store';
  8. export default function AsyncCounter () {
  9. const dispatch = useDispatch()
  10. const count = useSelector((state) => {
  11. return state.counter.count
  12. });
  13. function add () {
  14. dispatch({
  15. type: 'add',
  16. payload: 2,
  17. });
  18. }
  19. function asyncAdd () {
  20. // dispatch((next)=>{
  21. // //next参数其实就是dispatch,在底层传入的
  22. // setTimeout(()=>{
  23. // next({
  24. // type: 'add',
  25. // payload:2,
  26. // })
  27. // },2000);
  28. // });
  29. // dispatch(asynAddA);
  30. dispatch(asyncAddA(3));//传递参数
  31. }
  32. return (
  33. <div>AsyncCounter {count}
  34. <button onClick={add}>add</button>
  35. <button onClick={asyncAdd}>async add</button>
  36. </div>
  37. )
  38. }
  39. //store/index.js
  40. //为了方便传递参数,所有需要在外面套一层函数
  41. export const asyncAddA = (n) => (dispatch) => {
  42. setTimeout(() => {
  43. dispatch({
  44. type: 'add',
  45. payload: n,
  46. })
  47. }, 2000);
  48. }