Redux Saga

最近相當常使用Redux Saga,簡單紀錄一下。

比較常見用來處理Redux非同步操作的Middleware有redux-thunk, redux-saga, redux-observable等。之前參與開發的Firefox Devtools使用的是修改版的redux-thunk。我在其中一個個人專案中也有試過redux-observable。使用Rxjs的感覺,是整個過去的開發觀念又被扭成另一個形狀😅。因為整套開發的觀念不同,當後續想要修改時常常遇到一些問題。目前則多使用redux-saga來處理非同步相關問題。

基本Redux State運作

先簡單回顧一下基本的Redux State運作。Redux統一儲存所有網頁前端的狀態(State)。只能透過發送Action來通知狀態的改變,並透過Reducer修改當前的狀態。

graph LR Reducer{Reducer} -.-> State(State) Disptch>Dispatch] -.-> |Action| Reducer

基本的Redux僅支援同步的操作。若想支援非同步操作(promise, async, API等)需要透過加裝Middleware來處理。Middleware的功用,是改變收到Action後的處理流程。Redux Saga即是其中一種功能強大的Middleware。

Redux Saga運作

Redux Saga的運作模型,起始自收到Action。Saga透過takeLatesttake監聽想處理的Action,並呼叫對應的generator function。在generator function中,可以用yield語句來完成各種非同步操作。

例如使用select語句來從目前的State中取得資料,使用call語句來呼叫API,用put語句來將新的Action傳回Redux處理。

graph LR TakeLatest>TakeLatest] -.-> |Action| Saga Saga -->|call| API State -->|select| Saga Put>Put] -.-> |Action| Reducer Saga --> Put

一段簡單的範例:

function* handleAction(action) {
// get state from state.auth
const auth = yield select((state) => state.auth)
// async get data. fetchData is the async function.
const { result } = yield call((auth) => fetchData(auth))
// dispatch action. saveData is the action creator.
yield put(actions.saveData(result))
}

export default function* rootSaga() {
yield takeLatest('ACTION', handleAction)
}

當然Redux-saga還有提供一些其他的功能,但其實Redux-saga就是這麼簡單。使用Redux-saga可以很好地運用原有的非同步處理觀念,要將React Component中相應的非同步函式搬出來也不像Redux-observable那樣需要全部改寫。

參考資料