Redux action not being fired inside a callback function - reactjs

I'm using react with redux and saga as middleware. Below is a sample generator function that is being fired upon calling regarding action
function* createRoom({ payload }) {
try {
// block of code
}
} catch (error) {
handleError(error, (errorMessage: any) => {
console.log(errorMessage);
createRoomFailure(errorMessage);
});
}
}
handleError function
const handleError = (error, errorHandler) => {
if (error.response) {
const { data, config } = error.response;
console.log(
`${data.type} on method ${config.method} at ${config.baseURL}${config.url}`,
);
if (data.type === 'Network Error') {
errorHandler('Network Error');
} else if (data.status === 400) {
errorHandler('Bad Request');
} else if (data.status === 401) {
errorHandler(
'Unauthorized user. Please enter valid email and password.',
);
} else if (data.status === 403) {
errorHandler('Access Error');
} else if (data.status === 404) {
errorHandler('Method Not Found');
window.location.href = '/notFound';
} else if (data.status === 409) {
errorHandler('Duplicate Value');
} else {
errorHandler(data.type);
}
}
};
export default handleError;
but the problem is in the callback function, I can see the errorMessage in the console when I log it, but when I call the createRoomFailure action, it doesn't get fired.
Here is the createRoomFailure action
export const createRoomFailure = (errorMessage: any) => ({
type: RoomActionTypes.CREATE_ROOM_FAILURE,
payload: errorMessage,
});
can anyone tell me what's wrong here?

Action creators, such as createRoomFailure don't do anything by themselves outside of creating the action object. So if you just call the function of course nothing is going to happen.
What you need to do is to dispatch the action - that way redux can become aware of the returned object from the action creator and process it further.
You can dispatch actions in redux-saga using the put effect. But there is still the issue that you can not use effects outside of sagas. So you can't just use yield put(...) inside of your callback error handler.
In this case, where it seems your errorHandler is a synchronous function, I would suggest just rewriting it so that it returns the error message as string instead of using callback:
const handleError = (error) => {
if (error.response) {
const { data, config } = error.response;
return `${data.type} on method ${config.method} at ${config.baseURL}${config.url}`;
// ...
}
};
function* createRoom({ payload }) {
try {
// block of code
}
} catch (error) {
const errorMessage = yield call(handleError, error);
yield put(createRoomFailure(errorMessage));
}
}
In case your handleError will need to be asynchronous at some point, you can rewrite it to return a promise, which sagas can wait on.

Related

How To Know If Dispatch Is Success or No In Redux Saga

So i already create the createMonsterStart and it will hit my API, my API is returning response code, and I want to alert success when the response code is 00 otherwise it will alert failed, how can i achieve that? here is my code:
const onSubmitHandler = () => {
dispatch(createMonsterStart(monster))
if(dispatch success){
alert("success")
}else{
alert("error")
}
}
And here is the redux saga code:
export function* createMonsterAsync({ payload: { monster } }) {
try {
const user = yield select(getUser)
const a = yield call(createMonster, user.user.token, monster)
if (a.error) {
yield put(createMonsterFailure(a.error))
return false
}
const monsters = yield call(fetchMonsterAsync)
yield put(createMonsterSuccess(monsters))
} catch (error) {
yield put(createMonsterFailure(error))
}
}

await of generator completing in redux-saga

I have code in component,
I need to get updated authorizedError value in function, but i get old value authorized error
// login component
const authorizedError = useSelector((state: RootState) => state.user.authorizedError);
const onSignInPress = useCallback(async () => {
await dispatch(userActions.postLoginUser({username: email, password}));
if (authorizedError) {
setNotificationErrors(['Wrong login or password'])
showNotification();
}
}, [authorizedError, validate, email, password]);
// postLoginUserSaga.js
export default function* postLoginUserSaga({
payload,
}: PayloadAction<UserCredentialsPayload>) {
try {
yield put(setSignInError(false));
const {
data: {
payload: { access_token },
status,
},
} = yield transport.post(URLS.postLoginUserURL, payload);
if (status !== "Ok") {
throw new Error(status);
}
yield setItemAsync(ACCESS_TOKEN_KEY, access_token);
yield put(setSignIn(true));
} catch (error) {
console.error("User login failed", error);
yield put(setSignInError(true));
}
}
// sagaRoot file
export default function* userRootSaga() {
yield all([
checkAuthSaga(),
takeEvery(actions.postLoginUser, postLoginUserSaga),
takeEvery(actions.postRegistrationUser, postRegistrationUserSaga),
takeEvery(actions.getProfileData, getProfileDataSaga),
]);
}
Redux actions don't return a promise, you can't use them like this.
If you want to use the promise API you can use the redux-thunk middleware which supports it.
If you want to use sagas you can add a callback action property instead.
// in component callback
dispatch(userActions.postLoginUser({username: email, password, callback: (authorizedError) => {
if (authorizedError) {
setNotificationErrors(['Wrong login or password'])
showNotification();
}
}));
// in saga
try {
...
action.callback();
} catch (err) {
action.callback(err);
}
Although that has its own issues.
Usually you communicate from sagas back to components by changing the redux state, so you can e.g. have a state for redux error, and based on that field show the error message or show different component if the login was succesful.

Possible Unhandled Promise Rejection: Error: Actions must be plain objects. Use custom middleware for async actions

I have a function that is meant to perform various asynchronous actions based on the set inputs. Here is my function:
const generalApiRequest =(requestUrl, urlType, data={},actionDispatch) =>{
return function(dispatch, getState){
console.log(dispatch);
adminId = getState().authentication.adminId;
token = getState().authentication.token;
return hitUrl(requestUrl,urlType, dispatch, data).then((response)=>{
try{
if(response.status === 200){
//dispatch(actionDispatch(response.data));
actionDispatch();
}
else{
console.log("Wrong response",response);
if(response.status === 401){
console.log('login again, auth failed');
dispatch(authFailed());
}
}
}
catch(error){
console.log(error);
}
}
,(error)=>{console.log(error)})
}
};
Here is also hitUrl() which is needed for the function :
const hitUrl= async function(requestUrl, urlType,dispatch, data={}){
try {
//const requestUrl = apiUrl+ 'application/fetch-dashboard-data'+`/{$adminId}`;
if(urlType === "get"){
response = await axios(requestUrl,header(token));
}
else {
response= await axios.post(requestUrl, data, header(token));
}
return response;
} catch (error) {
console.log(error);
console.log("error status", error.response.status);
try{
if(error.response.status === 401){
dispatch(authFailed());
}
}
catch(newError){
console.log(newError)
}
}
}
I also have the function processApplicant()
export const processApplicant=(data)=>{
let requestUrl;
let urlType = "post";
let message;
message = "The user has been successfully deleted"
requestUrl = apiUrl+ 'application/process/delete';
let actionDispatch= triggerSuccess(message);
generalApiRequest(requestUrl,urlType, data, actionDispatch);
}
Now I dispatched the action below in my react component
dispatch(processApplicant({adminId: 2, comment: "No comments", trigger: "Pick", userId: 3}));
On doing this I get the error in the title above (Possible Unhandled Promise Rejection: Error: Actions must be plain objects. Use custom middleware for async actions).
I have redux thunk as middleware and it works fine for other request. What I'm I doing wrong please?
Your processApplicant is not set correctly.
export const processApplicant = (data) => {
return (dispatch) => {
let requestUrl;
let urlType = "post";
let message;
message = "The user has been successfully deleted"
requestUrl = apiUrl + 'application/process/delete';
let actionDispatch = triggerSuccess(message);
dispatch(generalApiRequest(requestUrl, urlType, data, actionDispatch));
}
}

react redux Promise function to async/await

I have the following redux function adding new user to my database. It works fine but in event i introduce another call in my then, there could be need for extensive catching for everything.
What if we made it into async with try/Catch to handle all our errors ?
I tried a sample of but kept missing something.
Could someone arrange it for me please. Thanks.
export function newUser(values) {
return function(dispatch) {
const promise = axios.post(URL)
dispatch(createAdminUsersRequest(promise));
promise.then(
user => {
dispatch(createUsersSuccess(user));
dispatch(fetchUsers());
dispatch(switchUserActions(false, false, false));
},
function(error) {
if (error && error.response && error.response.data)
error = error.response.data;
if (error && error.data) {
error = error.data;
}
dispatch(createUsersFail(errors(error)));
setTimeout(() => dispatch(createUsersFail(null)), 6000);
}
);
return promise;
};
}
The conversion on promise to async-await is pretty straightforward. Firstly you declare the function as async by adding an async keyword to it. Secondly, you use await on the promise
export function newUser(values) {
return async function(dispatch) {
dispatch(createAdminUsersRequest(promise));
try {
const user = await axios.post(URL);
dispatch(createUsersSuccess(user));
dispatch(fetchUsers());
dispatch(switchUserActions(false, false, false));
} catch(error) {
if (error && error.response && error.response.data)
error = error.response.data;
if (error && error.data) {
error = error.data;
}
dispatch(createUsersFail(errors(error)));
setTimeout(() => dispatch(createUsersFail(null)), 6000);
}
};
}

React Native state getting empty after action REDUX_STORAGE_SAVE

I am new in react native and I am kind of stuck in redux. I am trying to fetch the vehicles from the api endpoint, I am using redux-saga, the request sent, successfully fetches the data and dispathes the FETCH_VEHICLE_SUCCESS action. and state gets updated in redux store. But after that an action(REDUX_STORAGE_SAVE as shown in the image below) is automatically triggered and it empties the state i.e vehicles(refer to the image below). Why is it happening?
I am using redux-logger for logging, as shown on the picture below the vehicles object gets empty.
saga.js
function fetchVehicleCall() {
console.log('here');
return new Promise((resolve, reject) => {
RegisterHelper.fetchAllVehicles()
.then(res => {
console.log('now');
resolve(res);
})
.catch(err => reject(err));
});
}
function* fetchVehicleRequest() {
while (true) {
yield take(GET_VEHICLE_REQUEST);
try {
const response = yield call(fetchVehicleCall);
yield put(fetchVehicleSuccess(response));
console.log('SAGA FETCH SUCCESS: ', response);
} catch (err) {
console.log('SAGA FETCH ERR: ', err);
}
}
}
export default function* root() {
yield fork(fetchVehicleRequest);
}
actions.js
export function fetchVehicleSuccess(vehicles) {
return {
type: FETCH_VEHICLE_SUCCESS,
vehicles,
};
}
reducer.js
case FETCH_VEHICLE_SUCCESS:
return Object.assign({}, state, {
vehicles: action.vehicles,
});

Resources