React從入門到放棄(4):Redux中介軟體

Never、C發表於2018-05-23

redux 提供了類似後端 Express 的中介軟體概念。
最適合擴充套件的是redux中的 store.dispatch 方法,中介軟體實際就是通過 override redux的store.dispatch() 完成
將 action -> reducer 過程變為 action -> middlewares -> reducer 如:

let next = store.dispatch;
store.dispatch = function dispatchAndLog(action) {
  console.log(`dispatching`, action);
  next(action);
  console.log(`next state`, store.getState());
}

新增中介軟體

redux提供了 applyMiddleware 方法便於新增中介軟體

applyMiddleware的原始碼:

export default function applyMiddleware(...middlewares) {
  middlewares = middlewares.slice()
  middlewares.reverse()

  // Transform dispatch function with each middleware.
  middlewares.forEach(middleware =>
    // 由於每次middle會直接返回返回函式,然後在這裡賦值給store.dispatch,
    // 下一個middle在一開始的時候,就可以通過store.dispatch拿到上一個dispatch函式
    store.dispatch = middleware(store)
  )
}

通過middleware將 store.dispatch 進行擴充套件

middleware會返回一個函式:return store => dispatch => action => {}

非同步操作

  1. 同步操作只要發出一種 Action 即可,非同步操作通常需要發出多種 Action(開始、成功、失敗)
  2. 非同步操作有2種Action寫法(3種type 或者 新增erro 和 response欄位)
  3. 非同步操作的state結構調整

Action寫法:

// 寫法一:名稱相同,引數不同
{ type: `FETCH_POSTS` }
{ type: `FETCH_POSTS`, status: `error`, error: `Oops` }
{ type: `FETCH_POSTS`, status: `success`, response: { ... } }

// 寫法二:名稱不同
{ type: `FETCH_POSTS_REQUEST` }
{ type: `FETCH_POSTS_FAILURE`, error: `Oops` }
{ type: `FETCH_POSTS_SUCCESS`, response: { ... } }

非同步State結構:

let state = {
  // ...
  isFetching: true,// 正在獲取資料
  didInvalidate: true,// 是否過期
  lastUpdated: `xxxxxxx`// 上次更新時間
};

使用中介軟體

  1. 使用createStore(reducer, enhancer)createStore(reducer, preloadedState, enhancer)
  2. applyMiddleware的引數為中介軟體,某些中介軟體有順序要求如:logger

redux-logger

redux-logger 可清晰記錄 preState action nextState time等資訊。

示例:

import { createStore, applyMiddleware } from `redux`
import createLogger from `redux-logger`
import rootReducer from `./reducers`

let store = createStore(rootReducer, applyMiddleware(createLogger));

redux-thunk

redux-thunk 用來優化redux中的非同步操作。

在store.dispatch 的方法引數只支援js物件(即Action),使用redux-thunk將支援引數為一個函式。

或者說 redux-thunk 使得 action 從一個物件變成一個函式。

函式簽名:(dispatch, getState) => {}

示例:

import { createStore, applyMiddleware } from `redux`
import thunkMiddleware from `redux-thunk`
import rootReducer from `./reducers`

let store = createStore(rootReducer, applyMiddleware(thunkMiddleware));

store.dispatch( dispatch => {
    dispatch({type:`CLICK_START`,data:res})
    fetch(`xx`)
        .then(res => dispatch({type:`CLICK_END`,data:res}));
} )

實際上,redux-thunk 的作用是讓 Action Creator方便可以返回函式,這樣讓專案中的同步非同步Action Creator呼叫可以保持一致

redux-saga

redux-saga 相比thunk 功能顯得全面,精細。
saga 是一個常駐程式,在複雜任務 及 長時事務場景非常適用。
這裡通過2個action 來展示 saga

示例:

import createSagaMiddleware, { delay } from `redux-saga`
import { all, put, takeEvery, takeLatest } from `redux-saga/effects`

function* helloSaga() {
    yield delay(1000)
    console.log(`hello world`)
}

function* incrementAsync() {
    yield delay(1000)
    yield put({ type: `click` })
}

function* rootSaga() {
    yield all([
        takeEvery(`hello`, helloSaga),
        takeLatest(`async`, incrementAsync)
    ])
}

let sagaMiddleware = createSagaMiddleware();

let store = createStore(rootReducer, applyMiddleware(sagaMiddleware))

sagaMiddleware.run(rootSaga);

store.dispatch({ type: `hello` });
store.dispatch({ type: `async` });

支援非同步的還有redux-promise

相關文章