How can i catch error? when i use redux-saga? - reactjs

How can i catch error? when i use redux-saga?
if error occred in const refreshresult = yield call(refresh);
i want to stop and throw this error action
i don't know what can i do
yield put({
type: REFRESH_FAILURE,
error: err.response.data,
});
how can i do that? how should i stop and send error action?
this is my code
function getPostAPI(data) {
return axiosInstace.post('/kakao/getpost', data);
}
function* getPost(action) {
try {
const result = yield call(getPostAPI, action.data);
yield put({
type: GETPOST_SUCCESS,
data: result.data,
});
} catch (err) {
if (err.response.data === 'jwtEx') {
const refreshresult = yield call(refresh); // <<<< if error occure refresh() i want to stop
yield put(action);
} else {
yield put({
type: GETPOST_FAILURE,
error: err.response.data,
});
}
}
}
function* refresh() {
try {
const result = yield call(refreshAPI);
yield AsyncStorage.setItem(
'accesstoken',
`${result.data.accessToken}`,
() => {
// console.log('accesstoken 재발급 저장 완료');
},
);
yield put({
type: REFRESH_SUCCESS,
data: result.data,
});
} catch (err) {
yield put({
type: REFRESH_FAILURE,
error: err.response.data,
});
}
}

I guess you're doing it the right way. Try and catch will work.
Check out this link for more info Redux-saga documentation

Related

dispatch an action after api call in redux-saga if some error occurs

Hi guys i was using redux saga in my project but i am stuck in part where i have to dispatch an action whenever an error occurs and then update the state in errorReducer
Here is my code
So as soon as user clicks on signup function we have the following code to run
function* signup(action) {
yield put({ type: TOGGLE_LOADING, payload: true });
yield call(userSignup, action.payload);
yield put({ type: TOGGLE_LOADING, payload: false });
}
then we have function userSignup() running
const userSignup = async (userData) => {
try {
console.log("api hittinh");
const response = await axios.post(`${BASE_HEADER_URL}/api/signup`, {
data: userData,
});
console.log({ response });
return response.data;
} catch (err) {
console.log("in catch");
console.log(err.response.data);
yield put({ type: ERROR, payload: err.response.data });
}
};
now as user tries to sign up with the email which already exists, i send status code of 409 'Unauthorized' and then it goes to catch statement, now how can i dispatch an action over here ?
I cant use yield here cause, it can be used inside generator function and neither can i use ''useDispatch' cause it says
Invalid hook call. Hooks can only be called inside of the body of a
function component
yield command can be used only generator functions(function*).
And you need to catch error in saga function.
function* signup(action) {
try {
put({ type: TOGGLE_LOADING, payload: true });
const response = yield call(userSignup, action.payload);
yield put({ type: TOGGLE_LOADING, payload: false });
} catch (err) {
yield put({ type: ERROR, payload: err.response.data});
}
}
const userSignup = async (userData) => {
try {
console.log("api hittinh");
const response = await axios.post(`${BASE_HEADER_URL}/api/signup`, {
data: userData,
});
return response.data;
} catch (err) {
throw new Error(err);
}
};

How to use yield put inside yield all

I want to make n number of api call and i am doing as floows
try {
....
yield all(
action.uploadDocsBody.map(u =>
call(uploadDocs, {
...,
}),
),
);
yield put({
type: UPLOADDOCS_COMPLETED,
payload: { statusText: 'Success' },
});
} catch (e) {
yield put({
type: UPLOADDOCS_FAILED,
payload: {
error: true,
},
});
}
the issue is that
UPLOADDOCS_COMPLETED being called only after finishing all the api calls.
I want to yield put after every api call how can use it ?
You can wrap each call with another generator, and yield it:
function *foo(u) {
yield call(uploadDocs, {...});
yield put({ type: UPLOADEDDOCS_COMPLETED, .....});
}
yield all(action.uploadDocsBody.map(u => foo(u)))
function call(uploadDocs,{...},callback){
/// do you task
callback()
}
function *foo(u) {
var put = function({ type: UPLOADEDDOCS_COMPLETED, .....});
yield call(uploadDocs, {...} ,put);
}

Why cannot catch error in redux-saga generator?

Here is store/store.js
...
const initSagaMiddleware = createSagaMiddleware();
const middlewares = [initSagaMiddleware, fetchPhotosMiddleware, putPhotoMiddleware];
const middlewareEnhancer = applyMiddleware(...middlewares);
...
initSagaMiddleware.run(rootSaga);
export default store;
sagas/api-saga.js
export default function* rootSaga() {
yield all([
photoWatcher()
]);
}
function* photoWatcher() {
yield takeEvery(PUT_PHOTO, putPhotoWorker);
}
function* putPhotoWorker(action) {
try {
const payload = yield call(putPhoto, action.urlParams, action.body);
yield put({ type: PHOTO_UPDATED, payload });
} catch (err) {
yield put({ type: API_ERROR_PUT_PHOTO, payload: err });
}
}
and services/api.js
export function putPhoto(urlParams, body) {
return axios.put('/abc/' + urlParams.key + '/def/' + urlParams.id + '/edit', body)
.then(res => {
return res.data;
})
.catch(err => err);
}
My problem is: even when I get error from an api call, redux-saga putting PHOTO_UPDATED instead of API_ERROR_PUT_PHOTO. What am I doing wrong? How to catch error?
The problem is you are trying to catch the same error twice. Just remove the catch form the putPhoto function and it should work.
export function putPhoto(urlParams, body) {
return axios.put('/abc/' + urlParams.key + '/def/' + urlParams.id + '/edit', body)
.then(res => {
return res.data;
});
}
Both then and catch promises in putPhoto func were resulting in try statement. Because of that, I give up on try...catch, instead I'm using if...else statement now. Like this:
function* putPhotoWorker(action) {
const payload = yield call(putPhoto, action.urlParams, action.body);
if (isResponseTypeWhatIExpected) {
yield put({ type: PHOTO_UPDATED, payload });
} else {
yield put({ type: API_ERROR_PUT_PHOTO, payload });
}
}

Infinite loop in Redux Saga

I see many questions about this but nothing from answers helps me to solve it. I wanted to see how saga works since I haven't worked with it.
Here is the code
export function* fetchItems() {
try {
yield put({ type: ITEMS_FETCH_REQUEST })
const response = yield call(fetch, 'https://jsonplaceholder.typicode.com/users');
const data = response.json();
yield put({ type: ITEMS_FETCH_SUCCESS, payload: { items: data } })
} catch (error) {
yield put({ type: ITEMS_FETCH_FAILURE, payload: { error: error.message }})
}
}
It calls an infinite loop, I tried a lot of things but nothing helps.
What am I doing wrong here ?
I figured it out, for watchAsyncSagaFunction generator function you need to create another constant that is different than the one that you call at the beginning of asyncSagaFunction.
Example:
export function* asyncSagaFunction() {
try {
yield put({ type: ITEMS_FETCH_REQUEST })
const response = yield call(fetch, 'https://jsonplaceholder.typicode.com/users');
const data = response.json();
yield put({ type: ITEMS_FETCH_SUCCESS, payload: { items: data } })
} catch (error) {
yield put({ type: ITEMS_FETCH_FAILURE, payload: { error: error.message }})
}
}
There is ITEMS_FETCH_REQUEST, you need to create another one, for example,ITEMS_FETCH and in a component call that one.
export function* watchAsyncSagaFunction() {
yield takeLatest(ITEMS_FETCH, fetchItems)
}

Redux saga multiple api calls

I have a bunch of action watchers of my api calls using redux-saga. The thing is I would like to make ONE action watcher which fire all these action watchers to fetch all the api without having to repeat the codes I already have. If one of the watcher return a rejected Promise, it should cancel all the other watchers. What's the best way of doing this?
function* watchFetchUsers() {
while(true) {
yield take([FETCH_USERS]);
try {
const users = yield call(api.fetchUserData);
yield put({ type:FETCH_USERS, payload: users });
} catch (err) {
yield put({ type:SIGNOUT, status: err });
return err;
}
}
}
function* watchFetchDepartments() {
while(true) {
yield take([FETCH_DEPARTMENTS]);
try {
const departments = yield call(api.fetchDepartmentData);
yield put({ type:FETCH_DEPARTMENTS, payload: departments });
} catch (err) {
yield put({ type:SIGNOUT, status: err });
return err;
}
}
}
function* watchFetchPositions() {
while(true) {
yield take([FETCH_POSITIONS]);
try {
const positions = yield call(api.fetchPositionData);
yield put({ type:FETCH_POSITIONS, payload: positions });
} catch (err) {
yield put({ type:SIGNOUT, status: err });
return err;
}
}
}
function* watchFetchBanks() {
while(true) {
yield take([FETCH_BANKS]);
try {
const banks = yield call(api.fetchBankData);
yield put({ type:FETCH_BANKS, payload: banks });
} catch (err) {
yield put({ type:SIGNOUT, status: err });
return err;
}
}
}
function* watchFetchAuthenticatedUser() {
while(true) {
yield take([FETCH_AUTHENTICATED_USER]);
try {
const user = yield call(api.fetchAuthenticatedUser);
yield put({ type:FETCH_AUTHENTICATED_USER, payload: user });
} catch (err) {
yield put({ type:SIGNOUT, status: err });
return err;
}
}
}
export default function* fetchData() {
yield [
fork(watchFetchUsers),
fork(watchFetchDepartments),
fork(watchFetchPositions),
fork(watchFetchBanks),
fork(watchFetchAuthenticatedUser)
];
}
How about this
export function* watchFetchAll() {
while(true) {
// const {type} = yield take(['FETCH_A', 'FETCH_B', ...]);
const {type} = yield take(action => /^FETCH_/.test(action.type));
console.log('type %s', type);
try {
const data = yield call(api.fetch, type);
console.log('data', data);
yield put({type, payload: data})
}
catch (error) {
console.log('error', error);
yield put({ type: 'SIGNOUT', status: error })
}
}
}
export default function* fetchData() {
yield *watchFetchAll();
}
The simple api implementation:
const api = {
fetch(type) {
switch (type) {
case 'FETCH_A': return Promise.resolve({result: 'Fetched A type'});
case 'FETCH_B': return Promise.resolve({result: 'Fetched B type'});
// other cases
default: console.log(`Unknown type ${type}`);
}
}
};
The forked task's error is propagated to parent tasks.
I'm not sure if the below is what you want. But maybe it will work.
function* watchFetchUsers() {
while(true) {
yield take([FETCH_USERS]);
const users = yield call(api.fetchUserData);
yield put({ type:FETCH_USERS, payload: users });
}
}
function* watchFetchDepartments() {
while(true) {
yield take([FETCH_DEPARTMENTS]);
const departments = yield call(api.fetchDepartmentData);
yield put({ type:FETCH_DEPARTMENTS, payload: departments });
}
}
function* watchFetchPositions() {
while(true) {
yield take([FETCH_POSITIONS]);
const positions = yield call(api.fetchPositionData);
yield put({ type:FETCH_POSITIONS, payload: positions });
}
}
function* watchFetchBanks() {
while(true) {
yield take([FETCH_BANKS]);
const banks = yield call(api.fetchBankData);
yield put({ type:FETCH_BANKS, payload: banks });
}
}
function* watchFetchAuthenticatedUser() {
while(true) {
yield take([FETCH_AUTHENTICATED_USER]);
const user = yield call(api.fetchAuthenticatedUser);
yield put({ type:FETCH_AUTHENTICATED_USER, payload: user });
}
}
export default function* fetchData() {
while (true) {
let tasks;
try {
tasks = yield [
fork(watchFetchUsers),
fork(watchFetchDepartments),
fork(watchFetchPositions),
fork(watchFetchBanks),
fork(watchFetchAuthenticatedUser)
];
yield join(...tasks)
} catch (e) {
yield cancel(...tasks);
yield put({ type:SIGNOUT, status: err });
}
}
}
Or if you don't want to restore tasks,
//....
export default function* fetchData() {
try {
yield [
fork(watchFetchUsers),
fork(watchFetchDepartments),
fork(watchFetchPositions),
fork(watchFetchBanks),
fork(watchFetchAuthenticatedUser)
];
} catch (e) {
yield put({ type:SIGNOUT, status: err });
}
}
The answer called Parallel Task wherein you call 2 or more api at the same time, also efficient to handle than to this (example below)
// wrong, effects will be executed in sequence
const users = yield call(fetch, '/users')
const repos = yield call(fetch, '/repos')
//instead, try this one
import { all, call } from 'redux-saga/effects'
const [users, repos] = yield all([
call(fetch, '/users'),
call(fetch, '/repos')
])
for more information regards with your issue, I prefer to read more about sync and async of redux saga, Redux-Saga Parallel task and Non-Blocking Call

Resources