Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An error thrown by a React component, being caught in an unrelated promise's catch block

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.

like image 234
Dmitry Shvedov Avatar asked Feb 11 '16 16:02

Dmitry Shvedov


People also ask

How do you use then to catch errors?

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.

Does catch return a promise?

The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.

What is .catch in Javascript?

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.

What does component did catch do?

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.


1 Answers

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 first catch block in the call stack. If no catch 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))
}
like image 55
Dmitry Shvedov Avatar answered Oct 21 '22 23:10

Dmitry Shvedov