Here's what I bumped into. Somewhere in a React component's render function, I have this:
{first_name} {last_name}
I replaced it with this:
{first_name.toUpperCase()} {last_name.toUpperCase()}
And my application could no longer log in. I'm using Axios to talk to the backend. Axios is promise-based. And after I made the change above. It apparently started executing both the then
and the catch
block of my login
API call. As I print the response in the catch
block.
function login(data, success, error) {
axios.post('/login',
JSON.stringify(data),
{
baseURL: config.apiUrl,
headers: { 'Content-Type': 'application/json; charset=utf-8' }
})
.then((response) => success(response.data))
.catch((response) => {
console.log(response)
error(response.status)})
}
I get the following:
TypeError: Cannot read property 'toUpperCase' of undefined
There is absolutely no relationship between the component above (it's a dumb component simply displaying stuff from props on the page) and the API calling code.
This is not the first time I bump into this issue in my app. Has anyone ever bumped into anything like this before? I understand that my variables in the component are undefined, and that's why the error comes up. My question is how does it make it into the catch block of the promise somewhere on the other end of the app.
In summary: then : when a promise is successful, you can then use the resolved data. catch : when a promise fails, you catch the error, and do something with the error information. finally : when a promise settles (fails or passes), you can finally do something.
The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.
A catch -block contains statements that specify what to do if an exception is thrown in the try -block. If any statement within the try -block (or in a function called from within the try -block) throws an exception, control is immediately shifted to the catch -block.
The componentDidCatch() method is invoked if some error occurs during the rendering phase of any lifecycle methods or any children components. This method is used to implement the Error Boundaries for the React application.
So it turns out that in JavaScript, any catch
block will catch any error that is being thrown anywhere in the code. And that is by design.
Here's the spec:
The
throw
statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the firstcatch
block in the call stack. If nocatch
block exists among caller functions, the program will terminate.
One workaround is to use a handler on all the promises that you introduce, that would console.log
anything that it didn't expect, such as this:
export function apiErrorHandler(handler) {
return (error) => {
if (error.stack) {
throw error
}
if (error.status && error.data) {
handler(error)
} else {
console.error('Unknown type of error thrown during service request or its handler:', error)
}
}
}
And than plug it into the axios service:
export function serviceGetContacts(success, error) {
axios.get(`/contacts`, cfg())
.then((response) => success(response.data))
.catch(apiErrorHandler(error))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With