最近相當常使用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
修改當前的狀態。
基本的 Redux 僅支援同步的操作。若想支援非同步操作(promise, async, API 等)需要透過加裝Middleware
來處理。Middleware 的功用,是改變收到Action
後的處理流程。Redux Saga
即是其中一種功能強大的 Middleware。
Redux Saga 運作
Redux Saga 的運作模型,起始自收到 Action。Saga 透過takeLatest
或take
監聽想處理的 Action,並呼叫對應的 generator function。在 generator function 中,可以用yield
語句來完成各種非同步操作。
例如使用select
語句來從目前的State
中取得資料,使用call
語句來呼叫 API,用put
語句來將新的 Action 傳回 Redux 處理。
一段簡單的範例:
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 state 與 Redux Saga 之間整體的呼叫關係。使用 Redux/Redux Saga 能將「從 UI 呼叫 -> 參看現有狀態來與 API 溝通 -> 根據取回值更新狀態 -> 更新 UI」這樣的複雜流程,整個簡化為單向的操作。
當然Redux-saga
還有提供一些其他的功能,但其實Redux-saga
就是這麼簡單。使用Redux-saga
可以很好地運用原有的非同步處理觀念,要將 React Component 中相應的非同步函式搬出來,也不像Redux-observable
那樣需要全部改寫。
參考資料
- https://redux-saga.js.org/ 或 https://redux-saga-in-chinese.js.org (中文)
- Polling with redux 比較使用 Redux-saga 和 Redux-observable 寫 polling 的差異