Implementing a Loading Middleware with Redux-Saga
·
1 min read
·
202
Words
·
-Views
-Comments
Some flows in our project take time. For a single API call, interceptors can toggle a loading spinner. But when an effect chains multiple requests and additional logic, API-level loading flickers on and off. We need loading control at the saga level.
Goal
When an action handled by a saga fires, show a loading mask. When the effect finishes, hide it.
Implementation
import { call, put } from 'redux-saga/effects';
import * as is from '@redux-saga/is';
const LOADING_BLACKLIST = [UserActionTypes.GET_USERS];
function isForkEffect(eff) {
return is.effect(eff) && eff.type === 'FORK';
}
function loading(sagaFn) {
return function* (action) {
function* loadingWrapper() {
yield put(loadingStatusAction(true));
yield call(sagaFn, action);
yield put(loadingStatusAction(false));
}
if (LOADING_BLACKLIST.includes(action.type)) {
return yield call(sagaFn, action);
} else {
// @ts-ignore
return yield call(loadingWrapper, action);
}
};
}
const effectMiddleware = (next) => (eff) => {
if (isForkEffect(eff)) {
eff.payload.args[1] = safe(eff.payload.args[1]);
eff.payload.args[1] = loading(eff.payload.args[1]);
}
return next(eff);
};
export const sagaMiddleware = createSagaMiddleware({
effectMiddlewares: [effectMiddleware],
onError: sagaEffectUnhandled
});
Notes
loading
is a higher-order saga. It wraps the original saga function with “loading on/off” dispatches.- Since saga functions are generators, the wrapper continues to return a generator.
Final Thoughts
Without middleware you’d repeat spinner toggling everywhere, violating DRY. This approach keeps the codebase clean.