# Redux Saga
## 簡述
- Reducers負責處理action的state更新;
- Sagas負責協調那些復雜或異步的操作。
## 安裝
npm install --save redux-saga
```
// ...
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
// ...
import { rootSaga } from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
const action = type => store.dispatch({type})
```
## 輔助函數
用來在一些特定的action被發起到Store時派生任務。
1. takeEvery
2. put: 用於創建dispatch Effect
3. take: 通過全面控制action觀察進程來構建復雜的控制流
4. fork: 無阻塞調用
---
```
yield fetch(url) => yield call(fetch, url)
```
### take
等待dispatch匹配某個action
```
while(true) {
yield take('Click_Action')
yield fork(clickButtonSaga)
}
```
### put
觸發某個action,作用和dispatch相同
```
yield put({type: 'CLICK'})
```
```
//具體例子
import { call, put } from 'redux-saga/effects'
export function* fetchData(action) {
try {
const data = yield call(fetch, url)
yield put({type: 'FETCH_SUCCESS', data})
} catch (error) {
yield put({type: 'FETCH_FAILED', error})
}
}
```
### call
有阻塞的調用saga或者返回promise的函數,只在觸發某個動作
### takeEvery
循環監聽某個觸發動作,通常會使用while循環替代
```
import {takeEvery} from 'redux-saga/effects'
function* watchFetchData() {
yield takeEvery('FETCH_REQUESTED', fetchData)
}
```
### takeLatest
對於觸發多個action的時候,只執行最后一個,其他的會自動取消
```
import { takeLatest } from 'redux-saga/effects'
function* watchFetchData() {
yield takeLatest('FETCH_REQUESTED', fetchData)
}
```
### fork和cancel
通常fork和cancel配合使用,實現非阻塞任務,take是阻塞狀態,也就是實現執行take的時候,無法繼續向下執行,fork是非阻塞的,同樣可以使用cancel取消一個fork任務
```
function* authorize(user, password) {
try {
const token = yield call(Api.authorize, user, password)
yield put({type: 'LOGIN_SUCCESS', token})
} catch(error) {
yield put({type: 'LOGIN_ERROR', error})
}
}
function* loginFlow() {
while(true) {
const {user, password} = yield take('LOGIN_REQUEST')
yield fork(authorize, user, password)
yield take(['LOGOUT', 'LOGIN_ERROR'])
yield call(Api.clearItem('token'))
}
}
```
當執行yield fork(authorize, user, password),同時執行yield take(['LOGOUT', 'LOGIN_ERROR'])
### 錯誤處理
我們假設遠程數據讀取因為某些原因失敗了,API函數API.fetch返回一個被拒絕(rejected)的Promise
```
import Api from './Api'
import { call, put } from 'redux-saga/effects'
// ...
function* fetchProducts() {
try {
const products = yield call(Api.fetch, '/products')
yield put({ type: 'PRODUCTS_RECEIVED', products })
} catch (err) {
yield put({ type: 'PRODUCTS_REQUEST_FAILED', err)
}
}
```
### takeEvery的使用
saga中的take並不支持action的循環調用,即遍歷數組執行action,為解決該問題,可以使用takeEvery來執行action,此時即可實現遍歷數組執行action。
```
import { call, put, takeEvery } from 'redux-saga/effects'
function* fetchUsr(action) {
const payload = action
try {
const user = yield call(api.getUser, payload)
yield put({ type: GET_ENTITIES_USER, user })
} catch (err) {
console.log('err: %o', err)
}
}
function* mySaga() {
//在每個 'GET_USER' action 被發起時調用 fetchUser
//允許並發(譯注:即同時處理多個相同的 action)
takeEvery(actions.GET_USER, fecthUser)
}
```