上一篇博客我們用理論和代碼實踐介紹了 使用redux 發(fā)起action 在reducers里生成state 然后重新渲染組件
Redux 的核心理念是嚴格的單向數(shù)據(jù)流板辽,只能通過 dispatch(action) 的方式修改 store,流程如下:
view -> action -> reducer -> store
但是在業(yè)務(wù)復雜的以及和api數(shù)據(jù)對接的過程中肯定會遇到大量的異步操作。我們?nèi)绾蝸斫鉀Q這些場景呢?
redux中間件
什么是redux中間件
redux中間件
這里我們先從redux的中間件
說起狸窘, 中間件录择,顧名思義:進行中間處理的物件寄纵。類似于面向?qū)ο缶幊痰腁OP編程思想(不了解AOP的可以忽略這句話)
簡單的說:中間件可以控制在store dispatch action之前和之后的業(yè)務(wù)邏輯诗箍。也就是說 中間件實現(xiàn)了改寫 store.dispatch 方法實現(xiàn)了action -> reducer的攔截的行為。
如果我們分別注冊三個中間件: 中間件A 中間件B 中間件C
那么
中間件A -> 中間件B-> 中間件C-> 原始 dispatch -> 中間件C -> 中間件B -> 中間件A
和異步處理的關(guān)系
綜上所述:中間件可以領(lǐng)過的改變 dispatch的時機挽唉,這樣我們就可以很方便的處理異步場景了滤祖。
因此各種 redux異步處理中間件應(yīng)運而生。比較知名的有redux-thunk
和redux-saga
瓶籽。
redux-thunk
redux-thunk中間件可以讓action創(chuàng)建函數(shù)先不返回一個action對象匠童,而是返回一個函數(shù),函數(shù)傳遞兩個參數(shù)(dispatch,getState),在函數(shù)體內(nèi)進行業(yè)務(wù)邏輯的封裝
function add() {
return {
type: 'ADD',
}
}
function addIfOdd() {
return (dispatch, getState) => {
const currentValue = getState();
if (currentValue % 2 == 0) {
return false;
}
//分發(fā)一個任務(wù)
dispatch(add())
}
}
詳細代碼可以查看分支:https://github.com/YahuiWong/react-native-typescript/tree/redux-thunk
redux-saga
sages 采用 Generator 函數(shù)來 yield Effects(包含指令的文本對象)塑顺。Generator 函數(shù)的作用是可以暫停執(zhí)行汤求,再次執(zhí)行的時候從上次暫停的地方繼續(xù)執(zhí)行。Effect 是一個簡單的對象严拒,該對象包含了一些給 middleware 解釋執(zhí)行的信息扬绪。你可以通過使用 effects API 如 fork,call裤唠,take挤牛,put,cancel 等來創(chuàng)建 Effect种蘸。( redux-saga API 參考)
如 yield call(fetch, '/products') 即 yield 了下面的對象墓赴,call 創(chuàng)建了一條描述結(jié)果的信息竞膳,然后,redux-saga middleware 將確保執(zhí)行這些指令并將指令的結(jié)果返回給 Generator:
// Effect -> 調(diào)用 fetch 函數(shù)并傳遞 `./products` 作為參數(shù)
{
type: CALL,
function: fetch,
args: ['./products']
}
與 redux-thunk 不同的是诫硕,在 redux-saga 中坦辟,UI 組件自身從來不會觸發(fā)任務(wù),它們總是會 dispatch 一個 action 來通知在 UI 中哪些地方發(fā)生了改變章办,而不需要對 action 進行修改锉走。redux-saga 將異步任務(wù)進行了集中處理,且方便測試纲菌。
dispacth({ type: 'FETCH_REQUEST', url: /* ... */} );
所有的東西都必須被封裝在 sagas 中挠日。sagas 包含3個部分,用于聯(lián)合執(zhí)行任務(wù):
worker saga
做所有的工作翰舌,如調(diào)用 API嚣潜,進行異步請求,并且獲得返回結(jié)果
watcher saga
監(jiān)聽被 dispatch 的 actions椅贱,當接收到 action 或者知道其被觸發(fā)時懂算,調(diào)用 worker saga 執(zhí)行任務(wù)
root saga
立即啟動 sagas 的唯一入口
? 如何使用?
首先庇麦,我們得在文件入口中加入 saga 中間件计技,并且啟動它,它會一直運行:
//...
import { createStore, applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';
import appReducer from './reducers';
import rootSaga from './saga';
//...
const sagaMiddleware = createSagaMiddleware()
const store=createStore(rootReducer,applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
);
然后山橄,就可以在 sagas 文件夾中集中寫 saga 文件了:
import {delay} from 'redux-saga';
import {put,takeEvery,all} from 'redux-saga/effects';
import {ADD} from './actionsTypes';
function* addSync(){
yield delay(1000);
yield put({type:ADD})
}
function* watchaddSync(){
yield takeEvery("addSync",addSync)
}
export default function* rootSaga(){
yield all([
watchaddSync()
])
}
在 redux-saga 中的基本概念就是:sagas 自身不真正執(zhí)行副作用(如函數(shù) call)垮媒,但是會構(gòu)造一個需要執(zhí)行作用的描述。中間件會執(zhí)行該副作用并把結(jié)果返回給 generator 函數(shù)航棱。
對上述例子的說明:
(1)引入的 redux-saga/effects 都是純函數(shù)睡雇,每個函數(shù)構(gòu)造一個特殊的對象,其中包含著中間件需要執(zhí)行的指令饮醇,如:call(fetchUrl, url) 返回一個類似于 {type: CALL, function: fetchUrl, args: [url]} 的對象它抱。
(2)在 watcher saga watchFetchRequests中:
首先 yield take('FETCH_REQUEST') 來告訴中間件我們正在等待一個類型為 FETCH_REQUEST 的 action,然后中間件會暫停執(zhí)行 wacthFetchRequests generator 函數(shù)朴艰,直到 FETCH_REQUEST action 被 dispatch观蓄。一旦我們獲得了匹配的 action,中間件就會恢復執(zhí)行 generator 函數(shù)祠墅。
下一條指令 fork(fetchUrl, action.url) 告訴中間件去無阻塞調(diào)用一個新的 fetchUrl 任務(wù)侮穿,action.url 作為 fetchUrl 函數(shù)的參數(shù)傳遞。中間件會觸發(fā) fetchUrl generator 并且不會阻塞 watchFetchRequests毁嗦。當fetchUrl 開始執(zhí)行的時候撮珠,watchFetchRequests 會繼續(xù)監(jiān)聽其它的 watchFetchRequests actions。當然,JavaScript 是單線程的芯急,redux-saga 讓事情看起來是同時進行的勺届。
(3)在 worker saga fetchUrl 中,call(fetch,url) 指示中間件去調(diào)用 fetch 函數(shù)娶耍,同時免姿,會阻塞fetchUrl 的執(zhí)行,中間件會停止 generator 函數(shù)榕酒,直到 fetch 返回的 Promise 被 resolved(或 rejected)胚膊,然后才恢復執(zhí)行 generator 函數(shù)。
最后想鹰,總結(jié)一下 redux-saga 的優(yōu)點:
(1)聲明式 Effects:所有的操作以JavaScript對象的方式被 yield紊婉,并被 middleware 執(zhí)行。使得在 saga 內(nèi)部測試變得更加容易辑舷,可以通過簡單地遍歷 Generator 并在 yield 后的成功值上面做一個 deepEqual 測試喻犁。
(2)高級的異步控制流以及并發(fā)管理:可以使用簡單的同步方式描述異步流,并通過 fork 實現(xiàn)并發(fā)任務(wù)何缓。
(3)架構(gòu)上的優(yōu)勢:將所有的異步流程控制都移入到了 sagas肢础,UI 組件不用執(zhí)行業(yè)務(wù)邏輯,只需 dispatch action 就行碌廓,增強組件復用性传轰。
詳細代碼可以查看分支:https://github.com/YahuiWong/react-native-typescript/tree/redux-saga 如果覺得有用,請Star
谷婆,謝謝慨蛙!
參考:
https://segmentfault.com/a/1190000007248878#articleHeader7