2

I'm trying to capture all errors concurring in react app including event handler errors using window.onerror the following way in App.js:

    componentWillMount() {
        this.startErrorLog();
    }

    startErrorLog() {
        const {history} = this.props;
        window.onerror = (message, file, line, column, errorObject) => {
            column = column || (window.event && window.event.errorCharacter);
            let stack = errorObject ? errorObject.stack : null;
            //trying to get stack from IE
            if (!stack) {
                stack = [];
                // eslint-disable-next-line
                let f = arguments.callee.caller;
                while (f) {
                    stack.push(f.name);
                    f = f.caller;
                }
                errorObject['stack'] = stack;
            }
            const error = {
                message,
                fileName: file,
                lineNumber: line,
                columnNumber: column,
                stack
            };
            if (process.env.NODE_ENV === 'production') {
                Sentry.captureException(error);
            }
            history.push('/error');
            return false;
        }
    }

Inspired from: can you catch all errors of a React.js app with a try/catch block?

My problem is that when an error pops App.js is not rendered anymore and it seems as if react unmounts everything from the root element and so nothing is being rendered so the App.js holding the routing is not rendering thus the Error page is not being rendered.

Any ideas why react unmounts everything?

Jorayen
  • 1,737
  • 2
  • 21
  • 52

1 Answers1

0

If an error happens during rendering React has no way to recover from it. The current state of your application is corrupted and not renderable and there is no way for react to know how to revert it to a valid state. Therefore as a last resort everything just gets unmounted.

However the is a component called ErrorBoundary that you can wrap a part or the whole tree in. It catches errors happening during rendering in the tree below it and lets you render e.g. an error page instead.

You can also execute any other code in response to an error occurring.

Note that an error boundary can not catch the follwing errors:

Error boundaries do not catch errors for:

  • Event handlers (learn more)
  • Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
  • Server side rendering
  • Errors thrown in the error boundary itself (rather than its children)

The design decision to unmount the tree has been commented on by the react development team as follows:

New Behavior for Uncaught Errors

This change has an important implication. As of React 16, errors that were not caught by any error boundary will result in unmounting of the whole React component tree.

We debated this decision, but in our experience it is worse to leave corrupted UI in place than to completely remove it. For example, in a product like Messenger leaving the broken UI visible could lead to somebody sending a message to the wrong person. Similarly, it is worse for a payments app to display a wrong amount than to render nothing.

This change means that as you migrate to React 16, you will likely uncover existing crashes in your application that have been unnoticed before. Adding error boundaries lets you provide better user experience when something goes wrong.

For example, Facebook Messenger wraps content of the sidebar, the info panel, the conversation log, and the message input into separate error boundaries. If some component in one of these UI areas crashes, the rest of them remain interactive.

We also encourage you to use JS error reporting services (or build your own) so that you can learn about unhandled exceptions as they happen in production, and fix them.

trixn
  • 15,761
  • 2
  • 38
  • 55
  • I did not use `ErrorBoundary` so I could catch ALL errors. Is there a way to tell still keep react mounted because all I do is change route to an `ErrorPage` so that `react-router` will catch this – Jorayen Sep 04 '19 at 21:59
  • @Jorayen To change the route to e.g redirect to an error page is exactly what `ErrorBoundary` is made for so you should use that. And to answer your question: No it is not possible to keep the tree mounted when the error is happening during rendering because there is no way for react to bail out of the current render cycle where the error is occurring. The application is just in a state that is not renderable. If you want to also catch errors in event handler you will manually have to redirect to an error page though in the handler. – trixn Sep 04 '19 at 22:04
  • 1
    Also read [this official statement](https://reactjs.org/docs/error-boundaries.html#new-behavior-for-uncaught-errors) for why the react team chose to unmount the tree instead of aborting the render. – trixn Sep 04 '19 at 22:07