在Redux中常要管理非同步操作,目前社區流行的有Redux Saga、Redux thunk等。在管理複雜應用時,推薦使用Redux Saga,它提供了用 generator 書寫類同步代碼的能力。 在講解 ReduxSaga 前,先要說明一下 Redux Middleware 的概念。 Middl ...
在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中間件。
- 運行中間件。
感謝閱讀。