在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中間件。
- 運行中間件。
感謝閱讀。
