I have recently started using redux-saga and I'm really liking it.
I have the following wrapper which I was using for my api calls which would take a promise (my api call), and display a preloader and handle errors.
export const callApi = (promise: Promise<any>, errorMsg: string = 'Api error') => (dispatch: Dispatch) => {
dispatch(setLoading(true));
return promise.then(
(response) => {
dispatch(setLoading(false));
return response.body;
},
(error) => {
dispatch(setLoading(false));
dispatch(apiError(errorMsg, error));
return error;
});
};
I'm unsure how I would replicate behaviour like this in redux saga. I couldnt find any example of doing anything like this?
So far I've come up with
const camelizeKeysPromise = (obj) => Promise.resolve(camelizeKeys(obj));
export function* sagaCallApi(promise: Promise<any>, errorMsg: string = 'Api error') {
yield put(setLoading(true));
try {
const response = yield call(promise);
try {
const result = yield call(camelizeKeysPromise(response.body));
return result;
} catch (e) {
return response.body;
}
} catch (exception) {
yield put(setLoading(false));
yield put(apiError(errorMsg, error));
};
}
Yielding a call to promise will not return the desired response. You can use eventChannel from redux-saga to create a channel that emits the response on success or the error object on failure and then subscribe to the channel in your saga.
const promiseEmitter = promise => {
return eventChannel(emit => {
promise.then(
response => emit({response}),
error => emit({error})
);
});
};
Modify your new saga by replacing the call to the promise with this:
const channel = yield call(promiseEmitter, promise);
const {response, error} = yield take(channel);
if(response){
// handle success
return response;
}else if(error){
// handle failure
yield put(setLoading(false));
yield put(apiError(errorMsg, error));
}
Be aware that there might be syntactical errors in my code as I wrote this without an editor, but you can get the general approach.