Thoughts on software engineering
31 Jan 2020
Again, I admit I have done it, we all have done it. Another copy and paste job from some tutorial that written up in 2 minutes for the purpose of learning NOT fit for production code that is littered with try-catch.
Here is a contrived example:
export function* requestCat() {
try {
const resp = yield call(catService.get)
yield put(actions.requestCatSuccess(resp))
} catch (err) {
yield put(actions.requestCatError(err))
}
}
export function* requestDog() {
try {
const resp = yield call(dogService.get)
yield put(actions.requestDogSuccess(resp))
} catch (err) {
yield put(actions.requestDogError(err))
}
}
export function* catsNDogsSaga() {
yield takeLatest(actions.GET_CAT_REQUEST, requestCat)
yield takeLatest(actions.GET_DOG_REQUEST, requestDog)
}
As you can see, this is not very good for our mental healthy, let’s see if we can do it better.
In the case that we don’t want error to bubble up to root Saga, or we are interested in handling in a specific saga, we wrap the saga generator in a safe generator.
import * as actions from '../actions'
import { catService } from '../catServices'
import { put, call, takeLatest } from 'redux-saga/effects'
const safe = (handler:any = null, saga:any, ...args:any) => function* (action:any) {
try {
yield call(saga, ...args, action)
} catch (err) {
yield call(handler, ...args, err)
}
}
export function* requestCat() {
const cats = yield call(catService.get)
yield put(actions.requestCatSuccess(cats))
}
export function* requestDog() {
const dogs = yield call(catService.get)
yield put(actions.requestDogSuccess(dogs))
}
const onError = (err:any) => {
console.log(err)
}
export function* requestCatSaga() {
yield takeLatest(actions.GET_CAT_REQUEST, safe(onError,requestCat))
yield takeLatest(actions.GET_DOG_REQUEST, safe(onError, requestDog))
}
So we have to options here:
Add an onError function to the middleware option.
Chain a catch function at returned promise of sagaMiddleware.run()
import { createStore, applyMiddleware, compose } from 'redux'
import RootReducer from './reducers'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './sagas'
//option 1
const sagaMiddleware = createSagaMiddleware({
onError: (err) => {
store.dispatch({ type: 'ERROR', payload: err })
}
})
const composeEnhancers = ((window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
(window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, traceLimit: 25 }))
|| compose
const store = createStore(
RootReducer,
composeEnhancers(applyMiddleware(sagaMiddleware))
)
// option 2
sagaMiddleware.run(rootSaga).done.catch((e:any) => {
console.log({ message: e.message, source: 'sagaError', stacktrace: e.sagaStack })
store.dispatch({ type: 'ERROR', payload: e })
})
export default store
Okay thanks for reading if you have any suggestions please PR me on Github.