Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

_updateRenderedComponent Error When Rendered Child Components Repeatedly Throw #9511

Closed
dvdzkwsk opened this issue Apr 24, 2017 · 1 comment

Comments

@dvdzkwsk
Copy link

dvdzkwsk commented Apr 24, 2017

Apologies in advance if this has already been reported. I did my best to thoroughly search through existing issues before posting this, but could not find something this specific. I'd be happy to move/close this as needed. This is (hopefully) more nuanced than the issue about "my render method threw and React blew up, why?" that I mostly encountered.

Do you want to request a feature or report a bug?

Bug.

Minimal Test Cases

What is the current behavior?

If a child component throws in its render method, and attempts to be rendered more than once, React throws an uncaught error inside of its internal _updateRenderedComponent method. Depending on the context, this error is either:

  1. Uncaught TypeError: Cannot read property 'getNativeNode' of null
  2. Uncaught TypeError: Cannot read property '_currentElement' of null

Let's say we have a problematic component A. When A renders it throws a runtime error.

class A extends React.Component {
  render () {
    throw new Error('whoops')
  }
}
  1. Some parent component renders A. During the first render, we see the error that A threw while rendering. This is as expected and is a userland error.
  2. If we attempt to render the parent again, and therefore again render A, we get a different exception. This is where we see Uncaught TypeError: Cannot read property 'getNativeNode' of null from React (or the one referring to _currentElement).

The difference between exceptions #1 (getNativeNode) and #2 (_currentElement) is that #1 happens when component A is the same between each render; #2 happens when component A is swapped in the second render with another component that also happens to throw while rendering. The latter is more likely as we move from a loaded -> error handler view, where the error view may unfortunately also throw.

Why would you render a broken twice? Well, consider something as simple as this:

class AppLoaded extends React.Component {
  render () {
    throw new Error('I throw!')
  }    
}

// Supposed to gracefully render an error, but it happens to throw.
class GracefulError extends React.Component {
  render () {
    throw new Error('Dang, I also throw :(')
  }    
}

class Main extends React.Component {
  constructor(props, ctx) {
    super(props, ctx)
    this.state = {
      loaded: false,
      error: null,
    }
  }

  componentDidMount () {
    // Do some async work...
    // NOTE: This living inside of a promise is not vital to reproduction, but
    // it is what originally caused me to go down the path of discovering
    // the issue. See minimal test cases for examples w/o promises.
    return Promise.resolve() // do some async work
      .then(() => {
        this.setState({ loaded: true })
      })
      // Yes, this really should not be in `catch`, but there are valid scenarios
      // for code like this to exist. This is just to demonstrate the issue.
      // See explanations below.
      .catch((err) => {
        this.setState({ error: true })
      })
  }

  render () {
    if (this.state.error) return <GracefulError />
    if (this.state.loaded) return <AppLoaded />
    return <h1>Loading</h1>
  }
}

This is what led to the discovery of this bug. The promise handling here is technically incorrect, since what we really meant was to use .then(onSuccess, onFailure) so that we didn't implicitly catch synchronous render errors caused by the setState call in the onSuccess callback. However, I do believe there are valid scenarios where this occurs, such as when manually handling a render error, but where the recovering component rendered in its place also (unfortunately) throws and consequently leads to this untraceable error.

What is the expected behavior?

React should not blow up with this internal/untraceable error on the second render. I'm not even sure if this is possible, but at a minimum I'd like to better understand why this occurs.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

Browser: Google Chrome Version 58.0.3029.81 (64-bit)

React:

  • v15.1.0
  • v15.3.2
  • v15.5.0

Those are just the versions I've tested, there's no evidence that it works in any versions in between those posted above. No, this has not worked in a previous version to my knowledge.

Thank you in advance for your help.

@dvdzkwsk dvdzkwsk changed the title _updateRenderedComponent Error if Rendered Child Components Repeatedly Throw _updateRenderedComponent Error When Rendered Child Components Repeatedly Throw Apr 24, 2017
@dvdzkwsk
Copy link
Author

Closing since I now realize this has been reported and the React team is aware of it. Would have appreciated a link to spare me the rabbit hole, so in case others stumble upon this:

  1. Add runtime error overlay create-react-app#1101 (comment)
  2. https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/polyfills.js#L14-L19

And more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant