在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步。
- 建立
saga
並且將使用takeEvery
給每一個符合pattern
的action
都增加一個對應的saga處理函式。 - 使用
all
匯出編寫的saga。 - 建立saga中介軟體,在使用redux建立store時,應用saga中介軟體。
- 執行中介軟體。
感謝閱讀。