React: unstable_handleError - allow disabling of making the error bubble up to the parent upon retry

Created on 18 Jan 2017  Â·  7Comments  Â·  Source: facebook/react

The unstable_handleError feature #2461 , which allows for error boundaries in components, will attempt to re-render the component after the first error is handled in the boundary defined in unstable_handleError.

On this second render, by design, if an unhandled error occurs in the render, the error bubbles up to the parent:
https://github.com/facebook/react/blob/3bc5595dfd6db741b19f17e7aef98b2c1e4be847/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#L483

It would be better if we can configure this behavior - to be able to choose to have it bubble up, or be handled by the unstable_handleError upon retry.

In our case, it is acceptable to continue to retry over and over to render the screen and have a few errors, as there is the occasional race condition where the current state is not ready yet for the current component being rendered. It can error a few times until the state is ready.

Most helpful comment

Yes, potential for loops is why it's not the default behavior. This is still achievable though if you schedule another update in componentDidUpdate after recovering from error.

All 7 comments

We're unlikely to make this configurable. The mental model for how error boundaries work is a try-catch block:

try {
  renderChildren();
} catch (errorInChildren) {
  handleError();
  renderChildren();
}

The retry happens inside the catch block, so if it throws again, the error bubbles up to the next catch in the stack.

Making error boundaries configurable in the way you're asking would be like making catch blocks in JavaScript configurable. It's better to implement this feature in userland, rather than in the framework/language itself (React, in this case).

One way to implement this would be to remount the error boundary whenever it catches an error. Because the remounted component is a different instance from the one that caught the error, it will not be skipped on retry.

Here's an example using a withRemounting higher-order component. (You don't have to use an HOC but it's a good fit for this use case. See this doc for more information, in case you're unfamiliar.)

function withRemounting(WrappedComponent) {
  return class WithRemounting extends React.Component {
    state = { childKey: 1 };
    remount = () => {
      this.setState(state => ({
        childKey: state.childKey + 1
      }));
    }
    render() {
      return (
        <WrappedComponent
          {...this.props}
          key={this.state.childKey} // When the key changes, WrappedComponent is remounted
          remount={this.remount}
        />
      );
    }
  }
}


class ErrorBoundary extends React.Component {
  unstable_handleError() {
    this.props.remount();
  }
  render() {
    // ...
  }
}

const ErrorBoundaryWithRemounting = withRemounting(ErrorBoundary);

Disclaimer: I haven't actually run this code

Hope that helps!

Also with the new behavior in Fiber, I think retrying should "just work" as long as you render the error state at least once right after failing.

For example if you provide a "Retry" button for an error state, clicking it to clear the error should attempt to cleanly retry again, with the behavior you want. Note this will only work in Fiber and not in current version of React.

Great respond I will know how to get do it 1hr faster. This time maybe it
was better to do it manually and understand more. I'm not sure when it will
be ready - safe. Anybody can write to my to this mail for priv. Need small
advice/Help?

  • Priv data will beat least less .. huge :) I will do my best to do it
    right + will use Your advices. (Straight to this upper mail will be easier
    to read directions)

Have a good night

Andrew

18.01.2017 22:21 "Andrew Clark" notifications@github.com napisał(a):

We're unlikely to make this configurable. The mental model for how error
boundaries work is a try-catch block:

try {
renderChildren();
} catch (errorInChildren) {
handleError();
renderChildren();
}

The retry happens inside the catch block, so if it throws again, the
error bubbles up to the next catch in the stack.

Making error boundaries configurable in the way you're asking would be
like making catch blocks in JavaScript configurable. It's better to
implement this feature in userland, rather than in the framework/language
itself (React, in this case).

One way to implement this would be to remount the error boundary whenever
it catches an error. Because the remounted component is a different
instance from the one that caught the error, it will not be skipped on
retry.

Here's an example using a withRemounting higher-order component. (You
don't have to use an HOC but it's a good fit for this use case. See this
doc for more information
https://facebook.github.io/react/docs/higher-order-components.html, in
case you're unfamiliar.)

function withRemounting(WrappedComponent) {
return class WithRemounting extends React.Component {
state = { boundaryKey: 1 };
remount = () => {
this.setState(state => ({
boundaryKey: state.boundaryKey + 1
}));
}
render() {
return (
{...this.props}
key={this.state.boundaryKey}
remount={this.remount}
/>
);
}
}
}

class ErrorBoundary extends React.Component {
unstable_handleError() {
this.props.remount();
}
render() {
// ...
}
}
const ErrorBoundaryWithRemounting = withRemounting(ErrorBoundary);

Disclaimer: I haven't actually run this code

Hope that helps!

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/facebook/react/issues/8821#issuecomment-273604720,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AXwKLtPOuVXK-y4jnDbLazbq8r1wSSAtks5rToJKgaJpZM4LnVyn
.

JJ.[email protected] will be reopen in 15 min. Ty

18.01.2017 23:33 "Jędrzej Wrzesiński" jedrzejwrzesinski@gmail.com
napisał(a):

Great respond I will know how to get do it 1hr faster. This time maybe it
was better to do it manually and understand more. I'm not sure when it will
be ready - safe. Anybody can write to my to this mail for priv. Need small
advice/Help?

  • Priv data will beat least less .. huge :) I will do my best to do it
    right + will use Your advices. (Straight to this upper mail will be easier
    to read directions)

Have a good night

Andrew

18.01.2017 22:21 "Andrew Clark" notifications@github.com napisał(a):

We're unlikely to make this configurable. The mental model for how error
boundaries work is a try-catch block:

try {
renderChildren();
} catch (errorInChildren) {
handleError();
renderChildren();
}

The retry happens inside the catch block, so if it throws again, the
error bubbles up to the next catch in the stack.

Making error boundaries configurable in the way you're asking would be
like making catch blocks in JavaScript configurable. It's better to
implement this feature in userland, rather than in the framework/language
itself (React, in this case).

One way to implement this would be to remount the error boundary whenever
it catches an error. Because the remounted component is a different
instance from the one that caught the error, it will not be skipped on
retry.

Here's an example using a withRemounting higher-order component. (You
don't have to use an HOC but it's a good fit for this use case. See this
doc for more information
https://facebook.github.io/react/docs/higher-order-components.html, in
case you're unfamiliar.)

function withRemounting(WrappedComponent) {
return class WithRemounting extends React.Component {
state = { boundaryKey: 1 };
remount = () => {
this.setState(state => ({
boundaryKey: state.boundaryKey + 1
}));
}
render() {
return (
{...this.props}
key={this.state.boundaryKey}
remount={this.remount}
/>
);
}
}
}

class ErrorBoundary extends React.Component {
unstable_handleError() {
this.props.remount();
}
render() {
// ...
}
}
const ErrorBoundaryWithRemounting = withRemounting(ErrorBoundary);

Disclaimer: I haven't actually run this code

Hope that helps!

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/facebook/react/issues/8821#issuecomment-273604720,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AXwKLtPOuVXK-y4jnDbLazbq8r1wSSAtks5rToJKgaJpZM4LnVyn
.

@acdlite Thank you for the detailed response and suggestion. @gaearon in our case our app is displayed on a projector where the user does not interact with the screen, so a "retry" button would not be feasible. A similar solution we can try is automatically retrying after x time.

Hi,

Didn't run the code but @acdlite couldn't your retry logic lead to infinite loops if every remount attempt is failing?

Yes, potential for loops is why it's not the default behavior. This is still achievable though if you schedule another update in componentDidUpdate after recovering from error.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zpao picture zpao  Â·  3Comments

krave1986 picture krave1986  Â·  3Comments

UnbearableBear picture UnbearableBear  Â·  3Comments

zpao picture zpao  Â·  3Comments

trusktr picture trusktr  Â·  3Comments