How to handle errors in fetch() responses with Redux-Saga?
Don't handle the then
and error
in your fetchUser
method and your saga. Since you are already try
/catch
ing in your saga, you could handle it there.
Example
Saga
function* logIn(action) { try { const response = yield call(Api.logIn, action); if (response.status >= 200 && response.status < 300) { const user = yield response.json(); yield put({ type: types.LOG_IN_SUCCEEDED, user }); } else { throw response; } } catch (error) { yield put({ type: types.LOG_IN_FAILED, error }); }}
Fetch
fetchUser(action) { const { username, password } = action.user; const body = { username, password }; return fetch(LOGIN_URL, { method, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify(body) })}
As a side note: I find fetch
's api a little awkward because it returns a then
-able response when you make a request. There are many libraries out there; personally I prefer axios
which returns json by default.
if you want to have that if statement verifying the response status if(res.status >= 200 && res.status < 300) {
you need to have it inside your first promise where res is defined, it's currently inside the resolved promise for res.json()
.then(res => { if (res.status >= 200 && res.status < 300) { res.json().then(json => { return json } })})
If you need to make multiple API calls in one saga, the better approach is to throw errors at a fetch stage:
FETCH
export const getCounterTypes = (user) => { const url = API_URL + `api/v4/counters/counter_types`; const headers = { 'Authorization': user.token_type + ' ' + user.access_token, 'Accept': 'application/json' }; const request = { method: 'GET', headers: headers }; return fetch(url, request) .then(response => { return new Promise((resolve, reject) => { if (response.status === 401) { let err = new Error("Unauthorized"); reject(err); } if (response.status === 500) { let err = new Error("Critical"); reject(err); } if ((response.status >= 200 && response.status < 300) || response.status === 400) { response.json().then(json => { console.log(json); resolve(json); }); } }); });}
SAGA
export function* getMainScreenInfoSaga() { try { const user = yield select(getUser); const userInfo = yield select(getUserInfo); if (userInfo) { yield put({ type: types.NET_LOAD_USER_DATA }); } else { yield put({ type: types.NET_INIT }); } const info = yield all({ user: call(getInfo, user), apartments: call(getUserApartments, user), accounts: call(getUserAccounts, user), counters: call(getCounters, user) }); const ui = yield select(getUi); if (!ui) { yield put({ type: types.NET_LOAD_UI }); const ui = yield all({ apartmentTypes: call(getApartmentTypes, user), serviceTypes: call(getServiceTypes, user), counterTypes: call(getCounterTypes, user), }); yield put({ type: types.GET_UI_SUCCESS, ui }); } yield put({ type: types.GET_MAIN_SCREEN_INFO_SUCCESS, info }); yield put({ type: types.NET_END }); } catch (err) { if (err.message === "Unauthorized") { yield put({ type: types.LOGOUT }); yield put({ type: types.NET_END }); } if (err.message === "Critical") { window.alert("Server critical error"); yield put({ type: types.NET_END }); } }}