How can i stop redux action? when i get error - javascript

If an error occurs in the yield call(refresh) of the getPost function, the GETPOST_REQUEST action continues regardless of whether an error has occurred.
However, if an error occurs in the getPost function I don’t want the action to run anymore and stop and end like
yield put({
type: REFRESH_FAILURE,
error: err.response.data,
});
this is my code how can i fix?
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") {
yield call(refresh); // if this error i want to stop .
yield put(action);
} else {
yield put({
type: GETPOST_FAILURE,
error: err.response.data,
});
}
}
}
function refreshAPI() {
// console.log('data::', data);
return axiosInstace.post("/kakao/refresh");
}
function* refresh() {
try {
const result = yield call(refreshAPI);
yield AsyncStorage.setItem(
"accesstoken",
`${result.data.accessToken}`,
() => {
// console.log('accesstoken 재발급 저장 완료');
console.log("accesstoken3333333333333333333", result.data.accessToken);
}
);
yield put({
type: REFRESH_SUCCESS,
data: result.data,
});
} catch (err) {
console.log("refresh err.response.data:", err.response.data);
yield put({
type: REFRESH_FAILURE,
error: err.response.data,
});
}
}

You can make a small update to the code by doing the following:
return a boolean value from your refresh generator function.
The boolean would be:
true if refresh was successful
false if refresh failed.
set yield call(refresh); to a const declaration to capture this returned boolean value
if this captured boolean value is false, exit the generator function by returning early
Try the code below.
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 refreshSuccess = yield call(refresh);
if (!refreshSuccess) {
return;
}
yield put(action);
} else {
yield put({
type: GETPOST_FAILURE,
error: err.response.data,
});
}
}
}
function refreshAPI() {
return axiosInstace.post('/kakao/refresh');
}
/**
* #returns {boolean}
* - `true` if refresh is successful
* - `false` if refresh failed
*/
function* refresh() {
let refreshSuccess;
try {
const result = yield call(refreshAPI);
yield AsyncStorage.setItem(
'accesstoken',
`${result.data.accessToken}`,
() => {
// console.log('accesstoken 재발급 저장 완료');
console.log('accesstoken3333333333333333333', result.data.accessToken);
}
);
yield put({
type: REFRESH_SUCCESS,
data: result.data,
});
refreshSuccess = true;
} catch (err) {
console.log('refresh err.response.data:', err.response.data);
yield put({
type: REFRESH_FAILURE,
error: err.response.data,
});
refreshSuccess = false;
} finally {
return refreshSuccess;
}
}

Related

How can i use yield in redux-saga?

After receiving the result value of the refresh function, axiosInstace is executed before saving the accesstoken to AsyncStorage, so the updated token cannot be retrieved in axios.js through AsyncStorage.getItem. i want to save accesstoken first in refresh and get acecesstoken in axios.js and send to axiosInstace
How can I solve this problem?
this is my code
(saga.js)
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') {
yield put({
type: REFRESH_REQUEST,
// data: action.data,
});
yield put({
type: GETPOST_REQUEST,
data: action.data,
});
} else {
yield put({
type: GETPOST_FAILURE,
error: err.response.data,
});
}
}
}
function refreshAPI() {
return axiosInstace.post('/kakao/refresh');
}
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,
});
}
}
(axios.js)
AxiosInstance.interceptors.request.use(async (cfg) => {
const acecesstoken = await AsyncStorage.getItem('accesstoken');
const refreshtoken = await AsyncStorage.getItem('refreshtoken');
if (acecesstoken) {
cfg.headers.Authorization = `Bearer ${acecesstoken} ${refreshtoken}`;
}
return cfg;
});
export default AxiosInstance;
A simple solution would be to call your refresh() generator directly:
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') {
yield call(refresh);
// you could also redispatch the original action
yield put(action);
} else {
yield put({
type: GETPOST_FAILURE,
error: err.response.data,
});
}
}
}
Alternatively your can start a race between REFRESH_SUCCESS and REFRESH_FAILURE:
const { success, failure } = yield race({
success: take('REFRESH_SUCCESS'),
failure: take('REFRESH_FAILURE'),
});
if(success) {
// continue
} else {
// handle refresh failure
}

how can i use AsyncStorage data Everywhere?

I am using the saga library. And tokens are stored in AsyncStorage.
What I want is to freely use the token obtained from AsyncStorage in the loadUserPosts function or in loadPosts.
In this case, where should async be added and how do I fix the code?
this is my code
const token = await AsyncStorage.getItem('tokenstore');
function* loadUserPosts(action) {
try {
console.log(token)
yield put({
type: LOAD_USER_POSTS_SUCCESS,
data: result.data,
});
} catch (err) {
}
}
function* loadPosts(action) {
try {
console.log(token)
yield put({
type: LOAD_POSTS_SUCCESS,
data: result.data,
});
} catch (err) {
}
function* watchLoadPost() {
yield takeLatest(LOAD_POST_REQUEST, loadPosts);
}
function* watchLoadUserPosts() {
yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts);
}
export default function* postSaga() {
yield all([
fork(watchLoadPosts),
fork(watchLoadUserPosts),
]);
}
You can try and yield you async result. You might not even need async becuase generator function will yield until it gets a result.
function* loadUserPosts(action) {
try {
const token = yield AsyncStorage.getItem('tokenstore');
console.log(token)
yield put(LOAD_USER_POSTS_SUCCESS(token));
} catch (err) {
}

Why is React generator function with Yield not waiting?

In a React app, I do not understand why the Yield line in a generator function is not 'waiting'? Specifically, in the LOGIN function below, I would expect the Yield line immediately following console.log("Step 3") to pause until it was completed; however it does NOT pause and Step 8 is immediately processed. I would expect the STEPS in the console.log to follow the logical numerical order. The actual order that is printed out in the browser console window is: 1,2,3,8,9,10,4,5,6,7. Can someone explain why it is NOT pausing?
export function* LOGIN({ payload }) {
const { email, password } = payload
yield put({
type: 'user/SET_STATE',
payload: {
loading: true,
},
})
let userCog
try {
console.log("Step 1")
userCog = yield call(login, email, password)
console.log("Step 2")
} catch (err) {
if (err.code === 'UserNotConfirmedException') {
yield put({
type: 'user/SET_STATE',
payload: {
loading: true,
email,
},
})
yield history.push('/system/verification')
}
}
console.log("Step 3")
yield put({
type: 'user/LOAD_CURRENT_ACCOUNT',
})
console.log("Step 8")
if (userCog) {
console.log("Step 9")
yield history.push('/dashboard/analytics')
console.log("Step 10")
}
}
export function* LOAD_CURRENT_ACCOUNT() {
yield put({
type: 'user/SET_STATE',
payload: {
loading: true,
},
})
console.log("Step 4")
const response = yield call(currentUser)
console.log("Step 5")
if (response) {
const { username } = response
yield put({
type: 'user/SET_STATE',
payload: {
id: '123',
name: 'Administrator',
email: username,
role: 'admin',
authorized: true,
},
})
}
console.log("Step 6")
yield put({
type: 'user/SET_STATE',
payload: {
loading: false,
},
})
console.log("Step 7")
}
EDIT: Here is the redux dispatch from the Login UI Component
onSubmit = event => {
event.preventDefault()
const { form, dispatch } = this.props
form.validateFields((error, values) => {
if (!error) {
dispatch({
type: 'user/LOGIN',
payload: values,
})
}
})
}

How can I handle multiple dependent requests in a saga?

I'm trying to make two request, one to save an image an other to save a product with the url from obtained from the first requests
This is what I want to do
first: save product image (I'm using axios for requests)
second: get the url from 'productImage' and then include it in the params to save
this is my code
function* createProduct(action) {
const { endpoint, params } = action.payload;
try {
const productImage = yield call(new api().createImage, { endpoint, params });
// I need to wait the url of the image and include it on the params for the second request before is executed
// E.g. params.image = productImage.url
const product = yield call(new api().createProduct, { endpoint, params });
yield put({
type: CREATE_PRODUCT_SUCCEED,
payload: {
product
}
});
} catch (e) {
yield put({
type: CREATE_PRODUCT_FAILED,
payload: {
...e
}
});
}
}
export default function* createProductWatcher() {
yield takeEvery(CREATE_PRODUCT_EXECUTION, createProduct);
}
The best pattern here is to split your saga (createProduct) into two separate sagas:
createImage - will handle the image creation for the product
createProduct - will handle the product creation with the given image
// Creates the product image
function* createImage(action) {
const { endpoint, params } = action.payload;
try {
const image = yield call(new api().createImage, { endpoint, params });
yield put({
type: CREATE_IMAGE_SUCCEED,
// Here you pass the modified payload to createProduct saga
payload: {
endpoint,
params: { image: image.url }
}
});
} catch(e) {
yield put({
type: CREATE_IMAGE_FAILED,
payload: {
...e
}
});
}
}
//Creates the product with the image
function* createProduct(action) {
const { endpoint, params } = action.payload;
try {
const product = yield call(new api().createImage, { endpoint, params });
yield put({
type: CREATE_PRODUCT_SUCCEED,
payload: {
product
}
});
} catch(e) {
yield put({
type: CREATE_PRODUCT_FAILED,
payload: {
...e
}
});
}
}
Then use the builtin yield* operator to compose multiple Sagas in a sequential way.
// Another saga for chaining the results
function* invokeSagasInOrder(sagas) {
try {
const image = yield* createImage();
yield* createProduct(image);
} catch(e) {
console.log(e);
}
}
Welcome to stackoverflow!

TypeError: Cannot read property 'status' of undefined

I want to move device to created group(group device is shown only if any one device is selected) however i am getting an error of
TypeError: Cannot read property 'status' of undefined
Also in createGroupSuccess, updatedDevice object should be passed but when i console in createGroupSuccess action i get whole device object. That is why i could not add the group in the device object when trying to move.
function* createGroup(action) {
const device = yield select(selectDevice(), action);
const updatedDevice = { id: device.get('id') };
// const group = fromJS(action.group);
// if (action.deviceId) {
// group = device.getIn(['device_group', action.deviceId]);
// group = group.mergeDeep(action.group);
// console.log('group inside if block', group);
// }
updatedDevice.device_group = action.group;
if (action.group) {
yield call(POST(`/device/${updatedDevice.id}`, createGroupSuccess, createGroupError, updatedDevice));
}
}
function* rootSaga() {
yield takeEvery(CREATE_GROUP, createGroup);
}
export function createGroup(group, deviceId) {
return {
type: CREATE_GROUP,
group,
deviceId,
};
}
export function createGroupSuccess(device) {
console.log('group in success', device);
return {
type: CREATE_GROUP_SUCCESS,
device,
};
}
export function createGroupError(error) {
return {
type: CREATE_GROUP_ERROR,
error,
};
}
case CREATE_GROUP:
return state
.set('loading', true)
.set('error', null);
case CREATE_GROUP_SUCCESS:
console.log('success', action);
return state.set('loading', false).set('error', null)
.setIn(['devices', action.device.data.id, 'device_group', action.device.data]);
my device object is
device:Object
data:Object
description: null,
device_group: null,
id: "7eb006d6db50479aa47f887da0d4f10e",
name: "Fan Speed"
Where have i done the mistake in creating the group in the device object which is being selected while creating the group?
UPDATE
export function POST(apiUri, onSuccess, onError, data) {
return dataLoader(apiUri, onSuccess, onError, data);
}
export function dataLoader(apiUri, onSuccess, onError, data) {
return function* () { // eslint-disable-line func-names
const requestURL = `${API_BASE}${apiUri}`;
try {
let options;
if (data !== undefined) {
options = {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': Cookies.get('csrftoken'),
'X-Requested-With': 'XMLHttpRequest',
},
};
}
const response = yield call(requestJSON, requestURL, options);
yield put(onSuccess(response));
} catch (e) {
let error = null;
try {
error = yield call(() => e.response.json());
} catch (_) {
error = {
errors: [{
'code': e.response.status,
'msg': e.response.statusText,
}],
};
}
yield put(onError(error));
}
};
}
Try replacing status with sendStatus

Categories

Resources