react redux Promise function to async/await - reactjs

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);
}
};
}

Related

Test that errors are thrown in use Effect hook

I have a component that fetches data wrapped in a function to made async calls cancelable:
useEffect(() => {
const asyncRequest = makeCancelable(myService.asyncRequest());
asyncRequest.promise
.then((result) =>
setState(result),
)
.catch((e) => {
if (!e?.isCanceled) {
//Case the rejection is not caused by a cancel request
throw e;
}
});
return () => {
asyncRequest.cancel();
};
},[])
I want to test that, when the rejection is not coming from a cancel request, the error is re-thrown (I'm filtering out cancel rejections since they are not true errors). So the goal is intercept exceptions coming from useEffect
How can I test it with enzyme and/or jest?
it('should not filter rejection not caused by cancel', () => {
let promise = Promise.reject(new Error('Generic error'));
when(myService.asyncRequest()).thenReturn(promise); // This will cause useEffect to throw
const myComponent = mount(<MyComponent />) // How to intercept the error?
})
To give further context here is the code of makeCancelable:
export function makeCancelable<T>(promise: Promise<T>): CancelablePromise<T> {
let isCanceled = false;
const wrappedPromise = new Promise<T>((resolve, reject) => {
promise.then(
(val) => (isCanceled ? reject({ isCanceled: true }) : resolve(val)),
(error) => (isCanceled ? reject({ isCanceled: true }) : reject(error)),
);
});
return {
promise: wrappedPromise,
cancel() {
isCanceled = true;
},
};
}

Not able to get the response code in react js axios

Here is my common method for post:
export const post = (url: string, param: any) => {
const CancelToken = axios.CancelToken; // axios cancel request
const source = CancelToken.source();
post.prototype.source = source;
return axios.post(url, qs.stringify(param), { cancelToken:
source.token }).then((resp) => resp);
};
Here is my post method:
const postMyMethod = async () => {
await postMd(params)
.then((response: any) => {
console.log(response) // in response not getting status code
})
};
Below is the error handling code, how to get the response status code(ex: 200, 400...):
axios.interceptors.response.use(
function (response) {
if (response && response.data && response.data.Code && response.data.Message) {
message.error(response.data.Message);
response.data = null;
}
return response;
},
function (error) {
if (error.response && error.response.data && error.response.data.Code && error.response.data.Message) {
message.error(error.response.data.Message);
} else {
message.error('Unknown error, please check your network ~');
}
return error;
}
);
Finally if I do:
console.log(response)
Getting: Error: Request failed with status code 400
How to get the status code to do the if condition in the postMyMethod()?
I want to do like this in the postMyMethod(). How to achieve this?
if(response.status === 200){
// do something
}
if (respone.status === 400){
// do something
}
The error is because you are not using a catch() block in your postMyMethod function. You should add it so it will handle any error response. It will look something like this:
const postMyMethod = async () => {
await postMd(params)
.then((response) => {
console.log(response)
}).catch((err) => {
console.log(err.response.statuscode);
});
};
If response code 400 is something specific you want to handle differently in your function, your catch() block will be:
const postMyMethod = async () => {
await postMd(params)
.then((response) => {
console.log(response)
}).catch((err) => {
if (err.response.statuscode == 400) {
console.log(err);
} else {
console.log("something else");
}
});
};
You can read more about the catch() method here.
Finally got it:
.then((response: any) => {
console.log(response.response.status);
console.log(response.response.data);
})
or need to add below code under function error > if condition
return error.response;
Now getting the response status and failure data.

Redux action not being fired inside a callback function

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.

Axios refresh token issue

I'm using React.useEffect() to retrieve the users list.
React.useEffect(() => {
dispatch(UsersActions.creators.fetchingUsersAction());
UsersApi.methods.getUsers().then(
(res) => {
dispatch(UsersActions.creators.fetchUsersSuccessAction(res.data));
},
(e) => {
dispatch(UsersActions.creators.fetchUsersErrorAction());
}
);
}, [dispatch]);
On this example, fetchingUsersAction is used to set "loading" to true, and fetchUsersErrorAction to false. This works fine, except when the request fails due to token expiration.
ApiClient.interceptors.response.use(
function (response) {
return response;
},
function (error) {
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const refresh = JSON.stringify({
refreshToken: localStorage.getItem("refresh"),
});
AuthenticationApi.methods.refresh(refresh).then((res) => {
if (res.data.accessToken) {
localStorage.setItem("token", res.data.accessToken);
}
ApiClient.defaults.headers.common["Authorization"] =
"Bearer " + res.data.accessToken;
originalRequest.headers["Authorization"] =
"Bearer " + res.data.accessToken;
return ApiClient(originalRequest);
});
}
return Promise.reject(error);
}
);
This is sending a request to generate a new token and the previous request, but since the first request failed, the useEffect is going to the error section, making the "loading" false and showing the users list based on the previous state. What is the best way to deal with this problem?
Thanks
You should create an Async fucntion inside useEffect hook and use await to wait for the response, then call the function. Here is one example:
useEffect(() => {
const getRoles = async () => {
await authService.roles().then((res) => {
//Do your stuff.
console.log(res);
}).catch((error) => {
console.log(`'Catching the error: '${error}`);
});
};
//Call the recent created function.
getRoles();
}, []);
Your interceptor looks good to me.

Cannot read property 'showNotification' of undefined

i'm using create-react-app with PWA and just can't work notifications api :(
this error:
Cannot read property 'showNotification' of undefined
My code
Notification.requestPermission(function(status) {
console.log("Notification permission status:", status);
});
async function displayNotification() {
if (Notification.permission === "granted") {
await navigator.serviceWorker.getRegistration().then(reg => {
reg.showNotification("Go go")
});
}
}
I didn't understand the error
you cannot both await and use then. You can only use then on promises. And if you await, you resolve your promise.
you can do either :
async function displayNotification() {
if (Notification.permission === "granted") {
const reg = await navigator.serviceWorker.getRegistration();
reg.showNotification("Go go");
}
}
or :
function displayNotification() {
if (Notification.permission === "granted") {
navigator.serviceWorker.getRegistration().then(reg => {
reg.showNotification("Go go");
});
}
}

Resources