React: How does Error Boundary treat the thrown JS error?

Created on 24 May 2018  Â·  9Comments  Â·  Source: facebook/react

Do you want to request a feature or report a bug?
This is a question.

What is the current behavior?
The error boundary successfully intercepts the JavaScript errors and displays our fallback UI but it seems the JavaScript errors still gets thrown; they appear in red in the browser's console and they are caught in our Cypress tests, making them fail.

What is the expected behavior?
I don't know, that's what I'm asking. Does the Error Boundary throw the original JS error again? Or it lets JS throw it as it would normally do? Or does the Error Boundary catches the original thrown error and then prints it using console.error? _What is the designed behaviour for the Error Boundary regarding the original JavaScript thrown error?_

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
I use Google Chrome Version 66.0.3359.181 (Official Build) (64-bit) and React 16.3.2

Most helpful comment

In development environment, React uses a trick: caught exceptions are thrown inside a fake DOM event which makes them reported by window.onerror, but then React actually catches them so that they don't propagate up (in case there is an error boundary — otherwise they are rethrown).

In production errors caught by error boundaries stay caught.

All 9 comments

As is described in the doc

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.

I'm not quite sure about how the whole thing works under the hood, but when I searched the keywords in the codebase, this piece of snippets might give you some clue:

https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberUnwindWork.js#L121

w/r/t your question, I think the intention is that the Error Boundary catches the original thrown error and then prints it using console.error.

@cyan33 Thanks for your help, pointing me to the code was helpful; however, I'm still not sure.

It seems to me that the original error is still thrown, because Cypress of 2 reasons:

  1. this comment on this file:
 // We don't include the original error message and JS stack because the browser
 // has already printed it.

so the developer assumes the browser prints the original error (maybe it actually _throws_ it? That's what "prints" means?)

  1. Cypress caught the error in tests. I also console.loged something but Cypress did not catched that. As far as I read, Cypress listens to window.onerror, about which the docs say it gets triggered when _a JavaScript runtime error (including syntax errors and exceptions thrown within handlers) occurs_. I think the console.error is harmless but a not-silenced throw triggers the uncaught exception.

In development environment, React uses a trick: caught exceptions are thrown inside a fake DOM event which makes them reported by window.onerror, but then React actually catches them so that they don't propagate up (in case there is an error boundary — otherwise they are rethrown).

In production errors caught by error boundaries stay caught.

@gaeron I am just working on error reporting and got caught by that trick.
window.addEventListener('error') and window.onerror = (argh) => {} only works on development.

It is common in product code to report line and column numbers to your logging service, those values are only available on those 2 global handlers, leaving you with limited info from componentDidCatch first parameters, I know you could get more info when using a Babel plugin that is included in CRA, but it would be really helpful to expose that info in the error object.

Also, that Babel plugin doesn't show column number, only line number.

Related: https://github.com/facebook/react/issues/13790

@montogeek Babel plugin doesn't use source maps as browser console does. actually it doesn't help at all. that line number is useless.

Not strictly related but what happens if I throw error inside componentDidCatch? Will it propagate to window.onerror in production?

This has had me tearing my hair out for an hour or so.

I am a terrible person and have a react app without any error boundaries. Figured I would be lazy and just use a window error handler.

In development mode a component erroring triggers my window error handler, in production mode it does not?

I am not sure even the updated docs make this clear.

I opened an issue to discuss whether there's a way we can make errors behave identically in production and development: https://github.com/facebook/react/issues/19613

Was this page helpful?
0 / 5 - 0 ratings