Show loading spinner before axios fetches data using .get - javascript

When a user lands on their dashboard I want to load some widgets and I will be doing it via axios. I want to show a loading spinner while the data is being fetched. After some digging it seems like the way to do this is by using axios interceptors. (I think) It looks like I have it working but I don't know where I should be handling the response as doing it both ways works.
Here is the first way which works:
axios.interceptors.request.use((config) => {
console.log('Start Ajax Call');
FreezeUI();
return config;
}, (error) => {
console.log(error);
return Promise.reject(error);
});
axios.interceptors.response.use((response) => {
console.log('Done with Ajax call');
document.querySelector('.test').insertAdjacentHTML('afterbegin', response.data.html);
UnFreezeUI();
return response;
}, (error) => {
return Promise.reject(error);
});
axios.get('/account/active-listings')
.then(response => {
// Do I need this?
})
.catch(err => console.log(err));
And the second way that also works. But, it doesn't seem like I actually need that .then() and .catch() block at all?
axios.interceptors.request.use((config) => {
console.log('Start Ajax Call');
FreezeUI();
return config;
}, (error) => {
console.log(error);
return Promise.reject(error);
});
axios.interceptors.response.use((response) => {
console.log('Done with Ajax call');
return response;
}, (error) => {
return Promise.reject(error);
});
axios.get('/account/active-listings')
.then(response => {
document.querySelector('.test').insertAdjacentHTML('afterbegin', response.data.html);
UnFreezeUI();
})
.catch(err => console.log(err));

I'm not sure if I got it right, but what I think you are looking for is the onDownloadProgress option which you can add in your request config.
it is a function which you can implement which receives a progressEvent.
In that function you could set the spinner.
Take a look at the request config part on their github page
small example for clarity:
axios.get('/account/active-listings', {
onDownloadProgress: (pe) => document.querySelector('.place-to-insert-spinner').insertAdjacentHTML(loading spinner thingie)
})
EDIT: You probably should remove the spinner when your request is finished too.

Related

React-Redux - using a response from a fetch request in componentDidMount in another request in the component

I have a component that fires off a fetch request (in redux) on componentDidMount. In the same component I need to fire off another redux fetch request using the response data from the first one, preferably before the render.
Due to the fetch being executed in the redux action I havent been able to do this with a promise in componentDidMount as the promise resolves when the action starts and not finishes.
on research I thought it might be able to do it with componentWillRecieveProps however I don't fully understand how and also read that this hook is being depreciated soon.
any help would be greatly appreciated.
first action:
componentDidMount(){
this.props.onGetSessionId(this.parseToken(this.props.location.search))
};
secondAction:
this.props.onFetchFavourites(this.props.sessionId)
It's hard to help here without seeing the code for how onGetSessionId and onFetchFavourites are implemented but assuming that you're using redux-thunk, it's probably something like this:
function getSessionId(token) {
return function (dispatch) {
return fetchSessionId(token)
.then(
response => dispatch(getSessionIdSuccess(forPerson, response.id))
)
.catch(
error => dispatch(getSessionIdFailure(error)
)
};
}
function getFavorites(sessionId) {
return function (dispatch) {
return fetchFavorites(sessionId)
.then(
response => dispatch(getFavoritesSuccess(sessionId, response.favorites))
)
.catch(
error => dispatch(getFavoritesFailure(error)
)
};
}
fetch returns a promise so you can just keep chaining .then and using the return value from the previous then. this lets you do something like
function getSessionIdAndFavorites(token) {
return function (dispatch) {
return fetchSessionId()
.then(
response => {
dispatch(getSessionIdSuccess(response.id))
return response.id
}
)
.then(id => {dispatch(getFavorites(id)}
.catch(
error => dispatch(getSessionAndFavoritesIdFailure(error)
)
};
}
In onGetSessionId you can return promise, like this:
function onGetSessionId(param) {
return new Promise((resolve, reject) => {
apiClient.call()
.then(data => {
resolve(data);
})
.catch(error => {
reject(error)
})
})
}
and then in componentDidMount:
componentDidMount(){
this.props.onGetSessionId(this.parseToken(this.props.location.search))
.then(data => {
// ... do stuff with returned data
})
}

Wrap localForage setItem calls in Promise.all()

I want to refresh my indexeddb store with new data after a successful login. After the data refresh is complete, I want to redirect to the landing page. My problem is that I have 1000+ calls to setItem and they aren't finishing.
var app = {
Login: function () {
WebService.Login($("#username").val(), $("#password").val())
.then(function () {
// TODO: refresh data and then redirect...
UpdateData().then(function() {
window.location.href = '/Home';
});
})
.catch(function (err) {
console.log("error logging in");
});
},
UpdateData: function () {
return fetch('/api/Customer').then(function (response) {
return response.json();
})
.then(function (data) {
var customerStore = localforage.createInstance({ name: "customers" });
// Refresh data
customerStore.clear().then(function () {
data.forEach(function (c) {
// How do I know when all setItem calls are complete??
customerStore.setItem(String(c.CustomerID), c);
});
});
})
.catch(function (err) {
console.log("Data error", err);
});
}
}
I'm still relatively new to promises but there must be a way I can get all of the setItem calls into a Promise.all() that I can return. How can I do this?
I think that you need something like this:
return fetch("/api/Customer")
.then(function(response) {
return response.json();
})
.then(function(data) {
var customerStore = localforage.createInstance({ name: "customers" });
// Refresh data
return customerStore.clear().then(function() {
return Promise.all(
data.map(function(c) {
return customerStore.setItem(String(c.CustomerID), c);
})
);
});
})
.catch(function(err) {
console.log("Data error", err);
});
data.map will return an array of promises and then we also return the aggregate promise (from Promise.all).
You should also keep a reference of the customerStore for later use.
Also, if the amount of data is huge, you might want to use localForage-setItems to make the operation a bit more performant (but try to avoid a possible premature optimization).

Axios HTTP call is always treated as succeed

So I'm trying for multiple ways to get error response status from my axios HTTP call and something weird is happening.
getData() {
axios.get(`/api/article/getObserved.php`, axiosConfig)
.then(response => {
console.log('success');
console.log(response);
})
.catch(err => {
console.log('error');
console.log(err.status);
console.log(err.response.status)
});
}
So I'm calling my getObserved endpoint and although it's returning http_response_code(503); it's going to .then() part because it console log 'success' string.
this is output from console:
GET http://localhost/obiezaca/v2/api/article/getObserved.php 503 (Service Unavailable)
success favouriteArticles.vue?31bd:83
I've done hundreds of calls like this and this .catch was always catching error even tho I'm not throwing exception like in other lenguages I would do. However I also tried like this:
getData() {
axios.get(`/api/article/getObserved.php`, axiosConfig)
.then(response => {
console.log('success');
console.log(response);
}, function (err) {
console.log('error');
console.log(err.status);
console.log(err.response.status);
})
.catch(err => {
console.log('error');
console.log(err.status);
console.log(err.response.status)
});
}
But it still doesn't console 'error' although I have this 503 bad request returned from my endpoint. Why?
I also would like to add that I dont think my endpoint is not working correctly because I was testing it with tests and manually by cURL and POSTMAN and everything was fine.
Edit since response is undefined when I don't get data from my endpoint and I need to handle only one error (there is data or not) I have just do something like this:
getData() {
axios.get(`/api/article/getObserved.php`, axiosConfig)
.then(response => {
if(response) {
this.articles = response.data.records;
} else {
this.noFavourite = true;
this.articles = [];
}
});
and it's working. I'll pray to not get into same issue with some call where I'll need to handle several different errors.
This issue was related to my httpInterceptor
import axios from 'axios';
import { store } from '../store/store';
export default function execute() {
axios.interceptors.request.use(function(config) {
const token = store.state.token;
if(token) {
config.headers.Authorization = `Bearer ${token}`;
//console.log(config);
return config;
} else {
return config;
}
}, function(err) {
return Promise.reject(err);
});
axios.interceptors.response.use((response) => {
return response;
}, (err) => {
console.log(err.response.status)
return Promise.reject(err); // i didn't have this line before
});
}
which wasn't returning promise on error response so after in promise of http call it somehow treated it as success. After adding return Promise.reject(err); inside my interceptor it's working fine

Return from .catch(error) not returning in Node / Express

I'm writing a REST API and trying to correctly handle any errors.
When the API call succeeds, the the success object is returned to the calling function and the response is send to the client. But if an error occurs, I want to return the error to the calling function so I can send an error message to the client.
router.delete('/project', (req, res) => {
return DeleteProject(userId, projectId)
.then((response) => {
//handle response
});
});
DeleteProject: (userId, projectId) => {
return deleteProject(userId, projectId)
.then((response) => {
return response
})
.catch((error) => {
console.log('Error in DeleteProject:', error) // This happens.
return error; // this doesn't happen.
})
},
function deleteProject(userId, projectId) {
return Project.deleteOne( ... delete the project... )
.then((response) => {
return response
})
.catch((error) => {
return error
})
}
The .catch(error) in the middle function above, DeleteProject(), gets triggered when an error occurs (ie, the console log happens), but the return doesn't make it's way back to the router.
How can I return the error to be handled by the router?
You can simply remove catch methods from the other two functions, and put the catch function in the router itself. Then the error will itself propagate to your router function
router.delete('/project', (req, res) => {
return DeleteProject(userId, projectId)
.then((response) => {
//handle response
}).catch(() => {
// Add catch function here. Any error in "DeleteProject" and "deleteProject" will propagate to here
})
});
DeleteProject: (userId, projectId) => {
return deleteProject(userId, projectId)
.then((response) => {
return response
});
// Remove catch function
},
function deleteProject(userId, projectId) {
return Project.deleteOne( ... delete the project... )
.then((response) => {
return response
});
// Remove catch function
}
To propagate errors through promise chains you need to throw them. In your catch handler, when you return the error rather than throwing it, you'e setting the (successfully) resolved value of the promise to be the error.

react native fetch not calling then or catch

I am using fetch to make some API calls in react-native, sometimes randomly the fetch does not fire requests to server and my then or except blocks are not called. This happens randomly, I think there might be a race condition or something similar. After failing requests once like this, the requests to same API never get fired till I reload the app. Any ideas how to trace reason behind this. The code I used is below.
const host = liveBaseHost;
const url = `${host}${route}?observer_id=${user._id}`;
let options = Object.assign({
method: verb
}, params
? {
body: JSON.stringify(params)
}
: null);
options.headers = NimbusApi.headers(user)
return fetch(url, options).then(resp => {
let json = resp.json();
if (resp.ok) {
return json
}
return json.then(err => {
throw err
});
}).then(json => json);
Fetch might be throwing an error and you have not added the catch block. Try this:
return fetch(url, options)
.then((resp) => {
if (resp.ok) {
return resp.json()
.then((responseData) => {
return responseData;
});
}
return resp.json()
.then((error) => {
return Promise.reject(error);
});
})
.catch(err => {/* catch the error here */});
Remember that Promises usually have this format:
promise(params)
.then(resp => { /* This callback is called is promise is resolved */ },
cause => {/* This callback is called if primise is rejected */})
.catch(error => { /* This callback is called if an unmanaged error is thrown */ });
I'm using it in this way because I faced the same problem before.
Let me know if it helps to you.
Wrap your fetch in a try-catch:
let res;
try {
res = fetch();
} catch(err) {
console.error('err.message:', err.message);
}
If you are seeing "network failure error" it is either CORS or the really funny one, but it got me in the past, check that you are not in Airplane Mode.
I got stuck into this too, api call is neither going into then nor into catch. Make sure your phone and development code is connected to same Internet network, That worked out for me.

Categories