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

Error boundaries #5602

Merged
merged 1 commit into from
Dec 16, 2015
Merged

Error boundaries #5602

merged 1 commit into from
Dec 16, 2015

Conversation

jimfb
Copy link
Contributor

@jimfb jimfb commented Dec 4, 2015

Implements error boundaries for initial render, which allow React components to become isolation boundaries. If a child of an error bounadry crashes, the error boundary has the ability to handle the error and render something different (like an error message or a frown face). This allows application authors to partition their app into regions, and a crash in a single region won't take down the entire page/application.

Fixes #2461

);
}
handleError(e) {
this._errorMessage = e.message;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does setState work here? If not, can we make it work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setState does not work there. Among other things, we rollback the transaction after calling handleError. I'll try to figure out a way to make it work before merging.

@facebook-github-bot
Copy link

@jimfb updated the pull request.

@jimfb
Copy link
Contributor Author

jimfb commented Dec 11, 2015

Now with setState support :). @sebmarkbage @spicyj

if (inst.handleError) {
var checkpoint = transaction.checkpoint();
try {
markup = this.performInitialMount(renderedElement, nativeParent, nativeContainerInfo, transaction, context);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly out of curiosity.

according to: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#2-unsupported-syntax try/catch/finally make their containing function unoptimizable (in V8 at least).

Is there a reason you don't use the suggested workaround of isolating the try/catch/finally statements into minimal functions?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should probably do that – though I don't like making the stack deeper unnecessarily because it hampers debugging, and performance in the case that an exception is thrown is probably not a huge concern. Would be nice if the common path was broken up intelligently though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only adds a single stack frame to the boundary, not every component so I think it is probably fine. We should do it.

@facebook-github-bot
Copy link

@jimfb updated the pull request.

@facebook-github-bot
Copy link

@jimfb updated the pull request.

@jimfb
Copy link
Contributor Author

jimfb commented Dec 16, 2015

@sebmarkbage Now with a unit test to assert the event handlers (eg. onClick) never get added.

checkpoint = transaction.checkpoint();

this._renderedComponent.unmountComponent();
transaction.rollback(checkpoint);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is interesting. Can this actually happen? Doesn't matter if we do it as a precaution since we're already in a slow path. Just curious.

@sebmarkbage
Copy link
Collaborator

shipit

jimfb added a commit that referenced this pull request Dec 16, 2015
@jimfb jimfb merged commit 0c15b01 into facebook:master Dec 16, 2015
@jnak
Copy link

jnak commented Dec 16, 2015

YAYYYYY!!!

@slorber
Copy link
Contributor

slorber commented Dec 17, 2015

can't wait for the 0.15 release hahaha

@benjie
Copy link

benjie commented Dec 17, 2015

Awesome; great work! Really looking forward to trying this out!

@davidbarton
Copy link

Awesome thanks!

@bvaughn
Copy link
Contributor

bvaughn commented Dec 20, 2015

This is fantastic! :)

@mhart
Copy link

mhart commented Jan 31, 2016

Much of a performance hit with this?

@jimfb
Copy link
Contributor Author

jimfb commented Jan 31, 2016

@mhart Should be virtually none.

@mhart
Copy link

mhart commented Jan 31, 2016

@jimfb – cool, good to know! Thanks.

Now that I look at it in more detail it looks like the try/catch is happening at the mount stage, and not for each child render (is that correct?) – which certainly allays my initial fears.

@gaearon
Copy link
Collaborator

gaearon commented Feb 15, 2016

Is there an intention to also add error boundary checks for component updates?

I really want to kill react-transform-catch-errors but unfortunately the current error boundary implementation doesn’t satisfy my use case.

I am interested in catching errors occurring when a person hot reloads render() function and introduces an exception to it. I would then “retry” rendering the failed component on the next hot reload.

It would look like this:

feb 15 2016 18 54

Currently, error boundaries only handle “broken, later fixed” part of the equation, but not the “OK, but later broke”. From the technical point of view, it is equivalent to render() throwing only on the first update:

let calls = 0;

// ...

render() {
  calls++;
  if (calls === 1) {
    throw new Error('Yo');
  }
  return <NormalStuff />
}

If there is a desire to support this I can look into implementing this.

@ericclemmons
Copy link
Contributor

@gaearon's use-case is my own as well. With component playgrounds, it's easy for fixtures to be passed into a render() and generate an error, often making recovery require a refresh.

@jimfb
Copy link
Contributor Author

jimfb commented Feb 15, 2016

@gaearon @ericclemmons #6020

@slorber
Copy link
Contributor

slorber commented Feb 15, 2016

+1 i use hot reload and not yet "react-transform-catch-errors" and it's a pain because on any single render error due to bad transient code it requires an erroir even if I fix the code and hot-reload it

@AndrewIngram
Copy link

It feels like this is also the solution for implementing 404 and 401 views when using React Router. ie if a child route throws a special class of exception (eg NotFoundError or UnauthorizedError), any parent route can catch it and render the appropriate component. Assuming I'm right, when this feature is fully implementing, the biggest issue with using React Router will be solved.

@pluma
Copy link
Contributor

pluma commented Mar 18, 2016

@AndrewIngram you'd still need to detect the error state to set the proper response status when rendering on the server, though.

@tommoor
Copy link

tommoor commented Oct 1, 2016

Is there any intent to remove the unstable prefix and turn this into a documented API?

@gaearon
Copy link
Collaborator

gaearon commented Oct 1, 2016

It's not fully working yet: crashes in updates don't stop at the boundary. I intend to look into corresponding PR soon but API is likely to stay unstable_ until we use it for a while ourselves.

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

Successfully merging this pull request may close these issues.