redux處理異步action解決方案
如果沒有中間件,store.dispatch只能接收一個普通對象作為action。在處理異步action時,我們需要在異步回調(diào)或者promise函數(shù)then內(nèi),async函數(shù)await之后dispatch。
dispatch({
type:'before-load'
})
fetch('http://myapi.com/${userId}').then({
response =>dispatch({
type:'load',
payload:response
})
})
這樣做確實可以解決問題,特別是在小型項目中,高效,可讀性強。 但缺點是需要在組件中寫大量的異步邏輯代碼,不能將異步過程(例如異步獲取數(shù)據(jù))與dispatch抽象出來進行復(fù)用。而采用類似redux-thunk之類的中間件可以使得dispatch能夠接收不僅僅是普通對象作為action。例如:
function load(userId){
return function(dispatch,getState){
dispatch({
type:'before-load'
})
fetch('http://myapi.com/${userId}').then({
response =>dispatch({
type:'load',
payload:response
})
})
}
}
//使用方式
dispatch(load(userId))
使用中間件可以讓你采用自己方便的方式dispatch異步action,下面介紹常見的三種。
1. redux-thunk
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
2. redux-promise
使用redux-promise可以將action或者action的payload寫成promise形式。 源碼:
export default function promiseMiddleware({ dispatch }) {
return next => action => {
if (!isFSA(action)) {
return isPromise(action) ? action.then(dispatch) : next(action);
}
return isPromise(action.payload)
? action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
})
: next(action);
};
}
3. Redux-saga
redux-saga 是一個用于管理應(yīng)用程序 Side Effect(副作用,例如異步獲取數(shù)據(jù),訪問瀏覽器緩存等)的 library,它的目標是讓副作用管理更容易,執(zhí)行更高效,測試更簡單,在處理故障時更容易
3.1 基本使用
import { call, put, takeEvery, takeLatest} from 'redux-saga/effects';
//復(fù)雜的異步流程操作
function* fetchUser(action){
try{
const user = yield call(API.fetchUser, action.payload);
yield put({type:"USER_FETCH_SUCCEEDED",user:user})
}catch(e){
yield put({type:"USER_FETCH_FAILED",message:e.message})
}
}
//監(jiān)聽dispatch,調(diào)用相應(yīng)函數(shù)進行處理
function* mainSaga(){
yield takeEvery("USER_FETCH_REQUESTED",fetchUser);
}
//在store中注入saga中間件
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';
import reducer from './reducers';
import mainSaga from './mainSaga';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer,initalState,applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mainSaga)
3.2 聲明式effects,便于測試
為了測試方便,在generator中不立即執(zhí)行異步調(diào)用,而是使用call、apply等effects創(chuàng)建一條描述函數(shù)調(diào)用的對象,saga中間件確保執(zhí)行函數(shù)調(diào)用并在響應(yīng)被resolve時恢復(fù)generator。
function* fetchProducts() {
const products = yield Api.fetch('/products')
dispatch({ type: 'PRODUCTS_RECEIVED', products })
}
//便于測試
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
//便于測試dispatch
yield put({ type: 'PRODUCTS_RECEIVED', products })
// ...
}
// Effect -> 調(diào)用 Api.fetch 函數(shù)并傳遞 `./products` 作為參數(shù)
{
CALL: {
fn: Api.fetch,
args: ['./products']
}
}
3.3 構(gòu)建復(fù)雜的控制流
saga可以通過使用effect創(chuàng)建器、effect組合器、saga輔助函數(shù)來構(gòu)建復(fù)雜的控制流。
effect創(chuàng)建器:
- take:阻塞性effect,等待store中匹配的action或channel中的特定消息。
- put:非阻塞性effect,用來命令 middleware 向 Store 發(fā)起一個 action。
- call:阻塞性effect,用來命令 middleware 以參數(shù) args 調(diào)用函數(shù) fn。
- fork:非阻塞性effect,用來命令 middleware 以 非阻塞調(diào)用 的形式執(zhí)行 fn
- select:非阻塞性effect,用來命令 middleware 在當(dāng)前 Store 的 state 上調(diào)用指定的選擇器
effect組合器:
race:阻塞性effect:用來命令 middleware 在多個 Effect 間運行 競賽(Race)
function fetchUsersSaga {
const { response, cancel } = yield race({
response: call(fetchUsers),
cancel: take(CANCEL_FETCH)
})
}
all: 當(dāng) array 或 object 中有阻塞型 effect 的時候阻塞,用來命令 middleware 并行地運行多個 Effect,并等待它們?nèi)客瓿?/p>
function* mySaga() {
const [customers, products] = yield all([
call(fetchCustomers),
call(fetchProducts)
])
}
effect輔助函數(shù):
takeEvery:非阻塞性effect,在發(fā)起(dispatch)到 Store 并且匹配 pattern 的每一個 action 上派生一個 saga
const takeEvery = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel)
yield fork(saga, ...args.concat(action))
}
})
takeLatest:非阻塞性,在發(fā)起到 Store 并且匹配 pattern 的每一個 action 上派生一個 saga。并自動取消之前所有已經(jīng)啟動但仍在執(zhí)行中的 saga 任務(wù)。
const takeLatest = (patternOrChannel, saga, ...args) => fork(function*() {
let lastTask
while (true) {
const action = yield take(patternOrChannel)
if (lastTask) {
yield cancel(lastTask) // 如果任務(wù)已經(jīng)結(jié)束,cancel 則是空操作
}
lastTask = yield fork(saga, ...args.concat(action))
}
})
throttle:非阻塞性,在 ms 毫秒內(nèi)將暫停派生新的任務(wù)
const throttle = (ms, pattern, task, ...args) => fork(function*() {
const throttleChannel = yield actionChannel(pattern)
while (true) {
const action = yield take(throttleChannel)
yield fork(task, ...args, action)
yield delay(ms)
}
})
到此這篇關(guān)于redux處理異步action解決方案的文章就介紹到這了,更多相關(guān)redux 異步action內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于element?UI?input組件自行封裝“數(shù)字區(qū)間”輸入框組件的問題及解決
這篇文章主要介紹了基于element?UI?input組件自行封裝“數(shù)字區(qū)間”輸入框組件,實現(xiàn)代碼可以分為兩塊一塊為組件的封裝代碼,一塊為文中實現(xiàn)效果的演示代碼,對element?UI數(shù)字區(qū)間輸入組件相關(guān)知識感興趣的朋友一起看看吧2022-05-05
基于javascript代碼實現(xiàn)通過點擊圖片顯示原圖片
這篇文章主要介紹了基于javascript代碼實現(xiàn)通過點擊圖片顯示原圖片的相關(guān)資料,需要的朋友可以參考下2015-11-11
基于JavaScript實現(xiàn)圖片點擊彈出窗口而不是保存
這篇文章主要介紹了基于JavaScript實現(xiàn)圖片點擊彈出窗口而不是保存的相關(guān)資料,需要的朋友可以參考下2016-02-02

