Reputation: 788
I'm having some trouble understanding what I'm doing wrong. I have a function that receives a url to which should make a GET request, in case of success should fill a combo with the received data (this depends which function calls it), in case of fail it should execute some common code.
getFirstCombo = () => {
this.getFromApi('/First/GetAll')
.then(data => this.setState({firstComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
getSecondCombo = () => {
this.getFromApi('/Second/GetAll')
.then(data => this.setState({secondComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
parseCombo = (data: any) => {
const combo = data.map(item => (
{ label: item.description, value: item.id }
));
return combo;
}
getFromApi = (url: string) : Promise<any> => {
return restApiAxios.get(url)
.then(response => {
return response.data;
})
.catch(error => {
console.log('ERROR: ', error);
});
}
this code is executed on the componentDidMount of the react component, but when it fails, it first prints :
ERROR: Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
and immediately after:
PanelDatos.tsx:50 ERROR2: TypeError: Cannot read property 'map' of undefined
at PanelDatos.parseCombo (PanelDatos.tsx:55)
at PanelDatos.tsx:50
so, when failing executes the catch block from getFromApi and then it tries to execute the then block in getFirstCombo, which triggers the catch block from the same function cause data does not exist, why is that? shouldnt it just execute the first catch? thanks in advance
Upvotes: 2
Views: 336
Reputation: 73896
This is happening since inside getFromApi
catch method on the error you are not returning anything, so by default, it is returning a resolved promise with null
response and the execution goes inside getFirstCombo
then method, causing another error. You can update your code to resolve this like:
getFromApi = (url: string): Promise<any> => {
return restApiAxios.get(url)
.then(response => response.data)
.catch(error => Promise.reject(error));
}
The static Promise.reject
function returns a Promise
that is rejected. So, it will go directly into catch
of wherever getFromApi
is called.
DEMO:
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => Promise.reject(err))
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
.catch(error => console.log('ERROR2: ', error));
}
getFirstCombo()
DEMO #2 (With getFirstCombo
function not having any catch block) :
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => {
console.log('ERROR in getFromApi(): ', err);
return null; // return null, empty array, 0 or false... as per your requirement
})
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
// Same value set in catch block of getFromApi will return in this then() block
// Validate this `data` variable before processing it further like:
// if(data === null) this means an error had occurred
// else continue with your logic
}
getFirstCombo()
Upvotes: 1
Reputation: 269
.catch
returns a promise much like .then
, allowing you to return a custom value and handle it that way.
Try doing the following to observe the effect:
Promise
.reject(1)
.catch(e => e) // Catch the error and return it
.then(console.log) // will log 1 to the console
This means you'll need to add some checks if you want to continue to use promises like this:
Promise
.reject(new Error('haha'))
.catch(err => ({err}))
.then(({err, data}) => {
if(err) return // Do nothing
// enter code here
})
However, using async / await
will improve readability even more:
getFirstCombo = async () => {
let response
try {
response = await this.getFromApi('/First/GetAll')
} catch (e) {
return // Exit early
}
let parsed
try {
parsed = this.parseCombo(data)
} catch (e) {
console.log(e)
return // Exit early
}
return this.setState({firstComboOptions: parsed})
}
And, of course, throw the error again in your catch block in your api to allow it to handle api calls.
Upvotes: 2