Ionic loading controller keep presenting even after dismiss() called - angularjs

I have used the loading controller as a separate service, and called the present and dismiss methods inside http interceptor, but even after the request is released by interceptor and dismiss method is called, loading modal keep loading in UI,
interceptor code,
removeRequest(req: HttpRequest<any>) {
const i = this.requests.indexOf(req);
if (i >= 0) {
this.requests.splice(i, 1);
}
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.loadingCtrl.present();
this.requests.push(request);
return Observable.create(observer => {
const subscription = next.handle(request)
.subscribe(
event => {
if (event instanceof HttpResponse) {
this.removeRequest(request);
observer.next(event);
}
},
err => {
this.removeRequest(request);
observer.error(err);
this.toastor.presentToast(err.message);
},
() => {
this.removeRequest(request);
observer.complete();
this.loadingCtrl.dismiss();
});
return () => {
this.removeRequest(request);
subscription.unsubscribe();
};
});
}
}
loader controller service
export class LoadingService {
isLoading: boolean = false;
i = 0;
constructor(public loadingController: LoadingController) {}
async present() {
console.log('instance present ', this.i);
this.isLoading = true;
return await this.loadingController
.create({
message: 'Loading.......',
backdropDismiss: true,
})
.then((loader) => {
loader.present().then(() => {
if (!this.isLoading) {
loader.dismiss().then(() => {});
}
});
});
this.i = this.i + 1;
}
async dismiss() {
console.log('instance dismiss', this.i);
this.isLoading = false;
await this.loadingController.getTop().then((hasLoading) => {
if (hasLoading) {
return this.loadingController.dismiss().then(() => {});
}
});
this.i = this.i + 1;
}
}
Any idea why this happens ?

I had a similar problem with Ionic-React. In my case the dismiss executed before the present finished. Both are asynchronous, you should simply await present.

Related

How to stop React from finishing render when axios.interceptors.response handles the error?

I am working on a react app and I use tokens and refresh tokens for authentication. Whenever the backend returns a 401, the axios.interceptors.response picks it up and tries to refresh my token. If it succeeds, it will reinitiate the original call with the updated headers. See the code below:
// To avoid infinite loops on 401 responses
let refresh = false;
axios.interceptors.response.use(
(resp) => resp,
async (error) => {
if (error.response.status === 401 && !refresh) {
refresh = true;
const response = await axios.post(
"/api/auth/refresh",
{},
{ withCredentials: true }
);
if (response.status === 200) {
axios.defaults.headers.common[
"Authorization"
] = `Bearer ${response.data["accessToken"]}`;
return axios(error.config);
}
}
refresh = false;
return error.response;
}
);
This by itself works great, but not in combination with the code below in one of my components:
const [pages, setPages] = useState();
const [error, setError] = useState();
const navigate = useNavigate();
useEffect(() => {
async function fetchInfo() {
const response = await getMyPages();
if (response.status === 200) {
setPages(response.data);
}
else if (response.status === 401) {
setError(t("error.notAuthorized"));
navigate(`/login`, { replace: true });
}
// Any other error
else {
setError(t("error.unexpected"));
}
}
fetchInfo();
}, [t, navigate]);
// getMyPages function
export async function getMyPages() {
try {
const result = await axios.get(`/api/user/mypages`);
return result;
} catch (err) {
return err.response;
}
}
The problem is that the user is navigated to /login before the new request (with refreshed token) is made and finished. So when the new request finishes, I am not in the original component anymore and I can no longer update the pages state.
Any suggestions on how to handle this?
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
const getMyPages = async () => {
try {
const response = await axios.get(`/api/user/mypages`, {
signal: controller.signal
});
isMounted && setPages(response.data);
} catch (err) {
navigate(`/login`, { replace: true });
}
}
getMyPages();
return () => {
isMounted = false;
controller.abort();
}
}, [])

Why is this private class member out of scope at the point where I try to call it?

I want to be able to call "setUser", but it's out of scope for some reason. This is in a MobX store that I've created. I'm sure it's something I'm doing fundamentally wrong, but I don't know what it is. Here's the code:
private setUser = (user: UserType) => {
this.userRegistry.set(user.Username, user);
}
loadUsersFromPoolGroups = () => {
this.loadingInitial = true;
try {
var congitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
const USER_POOL_ID = 'us-east-2_kWOEamV6i';
var params = {
UserPoolId: USER_POOL_ID
}
congitoidentityserviceprovider.listGroups(params, function(err, data) {
if (err) console.log(err, err.stack);
else {
data.Groups.forEach(group => {
var params = {
GroupName: group.GroupName,
UserPoolId: USER_POOL_ID
}
congitoidentityserviceprovider.listUsersInGroup(params, function(err1, data1) {
if (err1) console.log(err1, err1.stack);
else {
data1.Users.forEach((user) => {
this.setUser(user);
})
}
})
})
}
});
} catch (error) {
console.log('error loading users from pool groups', error)
}
}
I'm doing a similar thing in a different store with no issues.
private setSubmittal = (submittal: Submittal) => {
this.submittalRegistry.set(submittal.submittalId, submittal);
}
loadSubmittals = async () => {
this.loadingInitial = true;
try {
const submittals = await agent.Submittals.list();
submittals.data.forEach((submittal: Submittal) => {
this.setSubmittal(submittal);
})
this.setLoadingInitial(false);
} catch (error) {
console.log(error);
this.setLoadingInitial(false);
}
}
I expected to be able to call setUser and it won't let me.

react how to redirect to a page if the user refresh (using f5) in a function component

redirect to a page
I already tried with the code below, but the alert message never appears, instead the message "reload site ?" is showed
and the redirect is not done
can anyone help me, thanks
const handler = useCallback((e) => {
alert("XXXXXXXXXXXX");
e.preventDefault();
e.returnValue = '';
return true;
}, []);
useEffect(() => {
window.onbeforeunload = handler;
return () => {
window.onbeforeunload = handler;
};
});
I solved the problem as follows, it's not perfect but...
also I put the handler in a class component
componentDidMount() {
if (window.addEventListener) {
window.addEventListener('beforeunload', this.onUnloadPage);
} else {
window.attachEvent('onbeforeunload', this.onUnloadPage);
}
}
componentWillUnmount() {
if (window.addEventListener) {
window.removeEventListener('beforeunload', this.onUnloadPage);
} else {
window.detachEvent('onbeforeunload', this.onUnloadPage);
}
}
onUnloadPage = (event) => {
window.setTimeout(function () {
window.location = '/collections/';
}, 0);
window.onbeforeunload = null;
};

How to make a PATCH request in ReactJS ? (with Nestjs)

nestjs controller.ts
#Patch(':id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
#Body('shippingAddr') addrShipping: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling, addrShipping);
return null;
}
nestjs service.ts
async updateProduct(
addressId: string,
addrBilling: boolean,
addrShipping: boolean,
) {
const updatedProduct = await this.findAddress(addressId);
if (addrBilling) {
updatedProduct.billingAddr = addrBilling;
}
if (addrShipping) {
updatedProduct.shippingAddr = addrShipping;
}
updatedProduct.save();
}
there is no problem here. I can patch in localhost:8000/address/addressid in postman and change billingAddr to true or false.the backend is working properly.
how can i call react with axios?
page.js
const ChangeBillingAddress = async (param,param2) => {
try {
await authService.setBilling(param,param2).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
return....
<Button size='sm' variant={data.billingAddr === true ? ("outline-secondary") : ("info")} onClick={() => ChangeBillingAddress (data._id,data.billingAddr)}>
auth.service.js
const setBilling = async (param,param2) => {
let adressid = `${param}`;
const url = `http://localhost:8001/address/`+ adressid ;
return axios.patch(url,param, param2).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}
I have to make sure the parameters are the billlingddress field and change it to true.
I can't make any changes when react button click
Since patch method is working fine in postman, and server is also working fine, here's a tip for frontend debugging
Hard code url id and replace param with hard coded values too:
const setBilling = async (param,param2) => {
// let adressid = `${param}`;
const url = `http://localhost:8001/address/123`; // hard code a addressid
return axios.patch(url,param, param2).then((response) => { // hard code params too
console.log(response); // see console result
if (response.data.token) {
// localStorage.setItem("user", JSON.stringify(response.data));
}
// return response.data;
})
}
now it worked correctly
#Patch('/:id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling);
return null;
}
const ChangeBillingAddress = async (param) => {
try {
await authService.setBilling(param,true).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
const setBilling= async (param,param2) => {
let id = `${param}`;
const url = `http://localhost:8001/address/`+ id;
return axios.patch(url,{billingAddr: param2}).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}

reactJS how to stop it listening to ajax request

I have ajax call in componentdidmount. And and then setState inside the ajax promise.
The code is like this
componentDidMount(){
axios.post('mydomian.com/item/',this.state)
.then(function (response) {
const res = response.data
if (res.status === 'OK') {
this.setState({items :res.list})
}else{
console.log('can not load data', response)
}
}.bind(this))
}
componentWillUnmount(){
how to stop everything about axios?
}
This causes error 'can not setstate on an unmounted component', when I navigate to other route.
So I think what I should do is remove axios listener in the componentwillunmount. How to would you do it?
A very simple solution could be to set a flag on unmount and utilize it within the promise resolution, like so:
componentDidMount(){
axios.post('mydomian.com/item/',this.state)
.then(function (response) {
if (this.unmounted) return;
const res = response.data
if (res.status === 'OK') {
this.setState({items :res.list})
}else{
console.log('can not load data', response)
}
}.bind(this))
}
componentWillUnmount(){
this.unmounted = true;
}
I have find a great solution that has been defined by istarkov
const makeCancelable = (promise) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then((val) =>
hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
);
promise.catch((error) =>
hasCanceled_ ? reject({isCanceled: true}) : reject(error)
);
});
return {
promise: wrappedPromise,
cancel() {
hasCanceled_ = true;
},
};
};
How to use:
const somePromise = new Promise(r => setTimeout(r, 1000));
const cancelable = makeCancelable(somePromise);
cancelable
.promise
.then(() => console.log('resolved'))
.catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));
// Cancel promise
cancelable.cancel();
the solution has been found there.
My implementation.
Inside my function
const promiseShareByEmail = makeCancelable(this.props.requestShareByEmail(obj.email, obj.url));
promiseShareByEmail.promise.then(response => {
const res = response.data;
if (res.code != 0)
throw new Error(res.message);
this.setState({
message: {
text: TextMeasurements.en.common.success_share_test,
code: Constants.ALERT_CODE_SUCCESS
}
});
}).catch(err => {
if (err.isCanceled)
return;
this.setState({
message: {
text: err.message,
code: Constants.ALERT_CODE_ERROR
}
})
});
this.promiseShareByEmail = promiseShareByEmail;
this.props.requestShareByEmail(obj.email, obj.url) that function returns promise from axios.
when component will unmount cancel function should be invoked.
componentWillUnmount() {
this.promiseShareByEmail.cancel();
}
enjoy.

Resources