Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do React components that throw errors render twice?

Tags:

reactjs

I've been experimenting with components that do a react-cache style thing and do web service calls right in the render method, throwing a promise up to a React.Suspense component and re-rendering when the data is there. They call a web service, check the response, and either render or throw an error up to an error boundary depending on the response. I've noticed that whenever an error is thrown in a component, it renders twice. The first time the callstack looks normal, and the second time the callstack includes calls to invokeGuardedCallbackDev and invokeGuardedCallback, which seem to have something to do with React ensuring that errors appear in the console even when "caught" by an error boundary in a dev build.

I can reproduce this with react and react-dom 16.8.6 by just rendering a component like this: https://codesandbox.io/s/components-that-throw-render-twice-i26qc.

I'm wondering why this happens, because it's causing the components to re-fetch data from the web service, re-throw another promise, and results in an "Uncaught Promise" error appearing in the console.

like image 603
ztforster Avatar asked Aug 29 '19 20:08

ztforster


2 Answers

This seems to be caused by a recent change in react/react-dom. If you revert both to version 16.0.0, you will see that it only renders the component once. See: https://codesandbox.io/s/components-that-throw-render-twice-03fdb

Looking at the version history, there seem to be a couple of bugs fixed relating to error handling in React, so it seems like this re-rendering is a result of a workaround for one of those bugs.

However, this should not be a problem for your app, as the render function should be pure (no side effects) in React apps. So basically, React can call your render function anytime it wants/needs to.

To work around this, you should avoid relying on the component not re-rendering and instead use an effect hook or similar to only fetch when certain props/state change.

Source: https://github.com/facebook/react/issues/16130#issuecomment-521637592

like image 69
omnidan Avatar answered Sep 19 '22 16:09

omnidan


I think the error boundary is not actually catching the thrown error.

From https://reactjs.org/docs/error-boundaries.html:

Note

Error boundaries do not catch errors for:

Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)

Asynchronous code includes Promises in this case.

See also https://reactjs.org/docs/error-boundaries.html#how-about-event-handlers:

Error boundaries do not catch errors inside event handlers.

If you need to catch an error inside event handler, use the regular JavaScript try / catch statement.

like image 42
jdoroy Avatar answered Sep 17 '22 16:09

jdoroy