Cancel an axios request, when not having the fetch in useEffect - react-native

I am trying to implement the cancel request function for my axios post. I created own js files for the functions and I am importing them into my screens. Here is an example
App.js
cancelToken.current = axios.CancelToken.source()
async function getFeed() {
let x = await service.getUserFeed(user_id, cancelToken);
setData(x); }
getFeed();
return () => {
cancelToken.cancel();
}
},[user_id]);
service.js:
getUserFeed: async (token, user_id, source) => {
let x;
await axios.post(Default.apiEndpoint + 'feed',
{
},
{
cancelToken: source.token,
headers: {
'Accept': "application/json",
'Content-Type': "application/json",
'user_id': user_id,
}
}).then(response => {
x = response.data;
}).catch(error => {
if (axios.isCancel(error)) {
console.log('Request canceled', error.message);
}
else {
x = error.response.status;
}
});
return x;
},
If I am calling the request in the hook itself it is working. So I am not even sure, if it is possible, because the request is not triggered, when the user leaves the page. So I would need to cancel the request in the service.js itself (I guess). Did anyone implement that already and can help me here?
Thanks

Related

How to properly await Nuxt calls with async/await or .then

Im trying to fetch an API using chaining with .then but I don't figure it out I try like:
async fetch() {
let path = this.$nuxt.context.route.path
this.response = await axios.get(
`/api${path}`,
{
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json'
}
}
).then((res) => {
this.results = res.data.content.slice(0,40);
return results();
})
.then((res) => {
this.results2 = res.data.content.slice(20,40);
return results2();
})
},
For my API data load: when results is finish /results2 start to load, for using it with $fetchState.pending
What will be the best way of doing it? I'm trying to adapt the answer from here but no success so far.
This kind of code should be working fine
<script>
export default {
async fetch() {
this.response = await axios
.get(`/api${this.$route.path}`, { // faster than this.$nuxt.context.route.path
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json',
},
})
.then((res) => { // ❌ this is useless because you're already using await above
const results = res.data.content.slice(0, 40)
return results()
})
.then((res) => { // ❌ this is useless because `slice` is NOT async
const results2 = res.data.content.slice(20, 40)
return results2()
})
},
}
</script>
Otherwise, I can also recommend a better approach overall, using async/await and not mixing it with .then at all, like this
<script>
export default {
async fetch() {
const response = await axios.get(
`/api${this.$route.path}`,
{
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json',
},
}
)
const results = response.data.content.slice(0, 40)
const results2 = results.data.content.slice(20, 40)
},
}
</script>
PS: note that some things are not async, hence do not need await (or .then at all).
It can even be shorten to the following
<script>
export default {
async fetch() {
const response = await this.$axios.$get( // 👈🏻 using the shortcut $get
`/api${this.$route.path}`,
{
headers: {
'X-AUTH-TOKEN': process.env.SECURE_TOKEN,
'Content-Type': 'application/json',
},
}
)
const results = response.content.slice(0, 40) // 👈🏻 no need for `.data` here
const results2 = results.content.slice(20, 40) // 👈🏻 same here
},
}
</script>
Thanks to the shortcuts available with the axios module that you should be using anyway.
As of why you should use async/await, it's just more lisible and available everywhere already (IE is dead). Here is some more info about the whole async/await thing: https://javascript.info/async-await
Far prettier than this kind of syntax (also named callback hell)

how can i add headers in vue js using async/await

i'm trying to send a request to the backend which uses headers, please how can i add the headers
this is my script tag
<script>
import axios from "axios";
export default {
data: () => ({
fullName: "",
streetAddress1: ""
}),
created() {
//user is not authorized
if (localStorage.getItem("token") === null) {
this.$router.push("/login");
}
},
methods: {
async onAddAddress() {
const token = localStorage.getItem("token");
headers: {
"Content-Type": "application/json",
Authorization: "Bearer" + token,
"x-access-token": token
}
try {
let data = {
fullName: this.fullName,
streetAddress: this.streetAddress1
};
const response = axios
.post("http://localhost:5000/api/addresses", data)
.then(res => {
console.log(res);
});
console.log(response);
} catch (error) {
console.error("error >>", error);
}
}
}
}
this code gives me an error, please how can i go about this
There are a few problems with your code. For instance you do not define headers as a variable and you do not add it to your axios request as a third argument. I think you need something like this:
async onAddAddress() {
const token = localStorage.getItem("token");
/// define headers variable
const headers = {
"Content-Type": "application/json",
Authorization: "Bearer" + token,
"x-access-token": token
};
let data = {
fullName: this.fullName,
streetAddress: this.streetAddress1
};
try {
/// config as the third argument.
conts result = await axios.post("http://localhost:5000/api/addresses", data, { headers });
console.log(result);
}
catch (error) {
console.error("error >>", error)
}
}
For async/await to work, you need to add await in front of the axios call.
Hope this helps.

axios cancellation caught inside of then() instead of catch()

I making a multi-upload file form.
Upon user cancellation, once the corresponding axios call get cancelled using cancel(), I having a weird behaviour. My axios call get caught inside the then() whereas it should be caught inside of catch(). The response inside of then() returns undefined.
I am having a hard time figuring if I did something wrong on the front-end part, I think my call is may be missing some headers or maybe it's on the backend part ?
const payload = { file, objectId: articleId, contentType: 'article' };
const source = axios.CancelToken.source();
// callback to execute at progression
const onUploadProgress = (event) => {
const percentage = Math.round((100 * event.loaded) / event.total);
this.handleFileUploadProgression(file, {
percentage,
status: 'pending',
cancelSource: source,
});
};
attachmentService
.create(payload, { onUploadProgress, cancelToken: source.token })
.then((response) => {
// cancelation response ends up here with a `undefined` response content
})
.catch((error) => {
console.log(error);
// canceled request do not reads as errors down here
if (axios.isCancel(error)) {
console.log('axios request cancelled', error);
}
});
the service itself is defined below
export const attachmentService = {
create(payload, requestOptions) {
// FormData cannot be decamelized inside an interceptor so it's done before, here.
const formData = new FormData();
Object.entries(payload).forEach(([key, value]) =>
formData.append(decamelize(key), value),
);
return api
.post(resource, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
...requestOptions,
})
.then((response) => {
console.log(response, 'cancelled request answered here as `undefined`');
return response.data;
})
.catch((error) => {
// not caught here (earlier)
return error.data;
});
},
};
cancellation is called upon a file object doing
file.cancelSource.cancel('Request was cancelled by the user');
As suggested by #estus-flask in a comment, the issue is that I was catching the error inside of the service (too early). Thank you!
export const articleService = {
create(payload, requestOptions) {
// FormData cannot be decamelized inside an interceptor so it's done before, here.
const formData = new FormData();
Object.entries(payload).forEach(([key, value]) =>
formData.append(decamelize(key), value),
);
return api.post(resource, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
...requestOptions,
});
},
};

Using Axios GET with Authorization Header in vue App

I'm trying to use axios for a GET request with an API which requires an Authorization header.
here is my current code
My current code:
data () {
return {
listings: [],
AuthStr : 'Bearer ' + JSON.parse(localStorage.getItem('token')),
}
},
created () {
axios.get(`url`, { 'headers': { 'Authorization': AuthStr } })
.then((response => {
this.listings = response.data;
})
.catch((error) => {
console.log(error)
})
}
it shows me 403 error I don't know why.
There are several ways to to add header to request.
For a single request:
let config = {
headers: {
Authorization: value,
}
}
axios.get(URL, config).then(...)
you need to call data().AuthStr to get your token there is a typo.
Your created function will be
created () {
axios.get(`url`, { 'headers': { 'Authorization': data().AuthStr } })
.then((response) => {
this.listings = response.data;
})
.catch((error) => {
console.log(error)
})
}
It should be:
axios.get(`url`, { 'headers': { 'Authorization': this.AuthStr } })
You are using JSON.parse when getting the value for AuthStr. JSON.parse returns an object. Try removing it and if you are using the correct Auth token, it should work.

AsyncStorage data changing upon app restart

I'm currently calling a JSON api to set an auth token which I'll just be storing in the AsyncStorage to persist between app life so a user doesn't have to log in every single time.
I'm currently setting that token like so:
fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(this.state)
})
.then( resp => {
return resp.json();
})
.then( async (data) => {
if ('error' in data) {
this.setState({
error: data.error,
password: ''
})
this.secondTextInput.focus();
}
if ('access_token' in data) {
try {
await AsyncStorage.setItem('access_token', data.access_token);
} catch (error) {
return error;
}
this.props.navigation.navigate('Main');
}
})
.catch(
error => {
console.error(error)
return error;
}
);
If I then call AsyncStorage.getItem('access_token') After killing the app or reloading it I'm winding up with this output:
{
"_40":0,
"_65":0,
"_55":null,
"_72":null
}
If I then call AsyncStorage.getItem('access_token') Before killing the app or reloading it I'm winding up with the correct access token. I've double checked the code and I'm not using AsyncStorage.setItem('access_token') anywhere else.
This is how I'm retrieving my token:
componentDidMount() {
console.warn('Mounting');
try {
let token = AsyncStorage.getItem('access_token');
console.warn(token);
if(token !== null) {
console.error(token);
}
} catch (error) {}
AsyncStorage.getItem() is a asynchronous action just like setItem(), so you need to wait until the Promise has been resolved before logging.
Edit
Tip: if you see some strange output like that it is always related to a Promise which is not yet resolved or rejected
I've solved my issue by using #dentemm's recommendation of creating an async function.
async _getToken() {
try {
var token = await AsyncStorage.getItem('access_token');
return token;
} catch(e) {
console.error(e);
}
}
componentDidMount() {
let token = null;
this._getToken()
.then( rsp => {
fetch(global.url + '/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + rsp
}
})
.then(rsp => {
return rsp.json();
})
.then(data => {
if('access_token' in data) {
try {
AsyncStorage.setItem('access_token', data.access_token);
} catch (error) {
return error;
}
this.props.navigation.navigate('Main');
}
})
.catch( error => {
return error;
})
});
}
This way I can get my token from the storage then run my refresh function to get an updated token to use for future requests.