react系列(六)Redux Saga

liuyongjia發表於2018-11-04

在Redux中常要管理非同步操作,目前社群流行的有Redux-Saga、Redux-thunk等。在管理複雜應用時,推薦使用Redux-Saga,它提供了用 generator 書寫類同步程式碼的能力。
在講解 ReduxSaga 前,先要說明一下 Redux Middleware 的概念。

Middleware

它提供的是位於 action 被髮起之後,到達 reducer 之前的擴充套件點。

如果寫過 Koa 或者 Express ,就會很容易理解 Middleware 的概念。
可以說,Middleware 是一種置於一個呼叫發起到被處理這段過程之間的函式。它可以對發起的呼叫進行處理,處理後直接返回,或者呼叫下一個中介軟體。
在Redux中,使用柯里化函式宣告中介軟體,一個簡單的例子如下:

/**
 * 記錄所有被髮起的 action 以及產生的新的 state。
 */
const logger = store => next => action => {
  console.group(action.type)
  console.info(`dispatching`, action)
  let result = next(action)
  console.log(`next state`, store.getState())
  console.groupEnd(action.type)
  return result
}

然後需要將它應用到Redux上

import { createStore, combineReducers, applyMiddleware } from `redux`

const todoApp = combineReducers(reducers)
const store = createStore(
  todoApp,
  // applyMiddleware() 告訴 createStore() 如何處理中介軟體
  applyMiddleware(logger, crashReporter)
)

之後dispatch的每一個action都會觸發log中介軟體。
更詳細的用法在Redux文件裡說明得很詳細了,Redux-Middleware

使用Redux Saga

先定一個小目標,寫一個非同步增加的demo。
先來建立一個sagas.js檔案,用來存放我們的sagas。

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

export function* plusAsync() {
  yield delay(1000)
  yield put({ type: `PLUS` })
}

// 在dispatch到store並且匹配pattern的每一個action上派生一個saga
export function* watchPlusAsync() {
  yield takeEvery(`PLUS_ASYNC`, incrementAsync)
}

在上篇例子的基礎上,增加一個按鈕,用來派發PLUS_ASYNC事件。

 <button onClick={dispatch({type: `PLUS_ASYNC`}}>{"plusAsync"}</button>

在使用時,經常需要將多個sagas合併成一個。

import { all } from `redux-saga/effects`

// ...

export default function* rootSaga() {
  yield all([
    watchPlusAsync()
  ])
}

最後,需要建立saga Middleware。並將中介軟體應用到redux上。

import { createStore, applyMiddleware } from "redux";

// ...

// 建立一個Store
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
  counter,
  applyMiddleware(sagaMiddleware)
)

// 執行sagas
sagaMiddleware.run(allSagas);

常用API說明

middleware.run(saga, ...args)

動態地執行 saga。只能 用於在 applyMiddleware 階段 之後 執行 Saga。

takeEvery(pattern, saga, ...args)

在發起(dispatch)到 Store 並且匹配 pattern 的每一個 action 上派生一個 saga。

pattern用來匹配對應的TYPE,對應到指定的saga處理函式上。
args就是相當於指定給saga的引數陣列,並且takeEvery會將action拼到最後一個引數上。

takeLatest(pattern, saga, ...args)

在發起到 Store 並且匹配 pattern 的每一個 action 上派生一個 saga。並自動取消之前所有已經啟動但仍在執行中的 saga 任務。

這個函式可以說是takeEvery的防抖版本。
具體例子可以檢視這裡-redux_saga_example

put(action)

建立一個 Effect 描述資訊,用來命令 middleware 向 Store 發起一個 action。 這個 effect 是非阻塞型的,並且所有向下遊丟擲的錯誤(例如在 reducer 中),都不會冒泡回到 saga 當中。

all

建立一個 Effect 描述資訊,用來命令 middleware 並行地執行多個 Effect,並等待它們全部完成。這是與標準的 Promise#all 相當對應的 API。

更多API請檢視Saga文件

總結

saga的用法比較簡單,分為4步。

  1. 建立saga並且將使用takeEvery給每一個符合patternaction都增加一個對應的saga處理函式。
  2. 使用all匯出編寫的saga。
  3. 建立saga中介軟體,在使用redux建立store時,應用saga中介軟體。
  4. 執行中介軟體。

感謝閱讀。

相關文章