Original link: reactjs.org/docs/error-…
The introduction
In the past, JavaScript exceptions within a component would cause state inside React to be corrupted and throw exceptions that might not be traceable during the next rendering. These errors were mostly caused by earlier code (non-React component code), but React didn’t provide a way to gracefully handle and respond to these exceptions in the component.
Error Boundaries
JavaScript exceptions in part of the UI should not crash the entire application. To address this issue, Act 16 introduced a new concept called Error Boundaries.
The exception catch boundary is a React component that catches any JavaScript exceptions that occur in its subcomponent tree, prints them out and presents an alternate UI so that the component tree doesn’t crash. The exception catch boundary can catch any exceptions that occur in its subcomponents in the rendering, lifecycle methods, and constructors.
Note: The exception catch boundary does not catch the following exceptions:
- Event Handling (learn more)
- Asynchronous code (e.g
setTimeout
orrequestAnimationFrame
Callback function)- Server side rendering
- Exceptions catch exceptions thrown by the boundary itself (not by its children)
As long as one or more of the lifecycle methods (static getDerivedStateFromError() or componentDidCatch()) are defined in the component, the component becomes an exception catch boundary. Use static getDerivedStateFromError() to render an alternate UI when an exception is thrown, and componentDidCatch() to print the exception.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false}; } static getDerivedStateFromError(error) {// Update the state and then display the standby UI in the next renderingreturn { hasError: true}; } componentDidCatch(error, errorInfo) {// You can also print exceptions in the exception reporting devicelogErrorToMyService(error, errorInfo);
}
render() {
if(this.state.hasError) {// You can render any custom alternate UIreturn <h1>Something went wrong.</h1>;
}
returnthis.props.children; }}Copy the code
Now you can use it as a normal component:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
Copy the code
The exception catch boundary works like JavaScript’s Catch {} module, but it catches exceptions thrown by components. Only class components can be exception catching boundaries. In practice, for the most part we just want to declare an exception catching boundary and then use it throughout the application.
Note that exception capture boundaries only catch exceptions thrown by components that are its children. But it does not catch the exceptions it throws. If an exception capture boundary fails to render successfully, it passes the exception information to its nearest ancestor exception capture boundary component. This is also similar to how the JavaScript Catch {} module works.
Where to place the exception catch boundary
The granularity of the exception capture boundary is up to you. You can wrap the top-level component to show the user that “something is wrong”, just as the server-side framework handles crashes. You can also wrap the widget in exception handling boundaries to prevent it from crashing into other parts.
New behavior that does not catch exceptions
This change has important implications. In Act16, any exception that is not caught by the exception capture boundary will cause the entire React component tree to be uninstalled.
There was some debate about this decision, but in our past experience, not dealing with broken UIs is worse than removing them altogether. In products such as social software, for example, a broken UI left on the interface can cause the user to send information to the wrong object. Similarly, displaying the wrong amount in the payment software has a bigger impact than showing nothing at all.
This change means that when you migrate to Act16, you may discover errors that you didn’t discover before. Using exception catching boundaries allows your application to provide a better user experience when exceptions occur.
For example, Facebook Messager wraps the sidebar, message panel, chat history, and message entry fields in separate exception capture boundaries, so that if one part crashes, it doesn’t affect the rest.
We also recommend using the JS error reporting service (or building your own) so that you can learn about exceptions that are not caught in production and fix them.
Component stack trace
In the development environment, Act16 prints all exceptions that occur during rendering to the console, even if the application has hidden them. In addition to exception information and JavaScript stacks, Act16 also provides component stack tracking. Now you can see exactly where in the component tree the exception occurred.
You can also see the file name and line number of the exception in the component stack trace. This is done by default in projects created through the Create React App.
If you are not using the Create React App, you can manually add the plugin to the Babel configuration. Note that this is only for use in the development environment and must be turned off in production.
Note: The component name shown in the stack trace depends on the function.name attribute. If you want to support browsers or devices that don’t already offer this functionality (such as IE11), consider adding a polyfill containing function.name to your packaged application, such as function.name-polyfill. Instead, you can explicitly set the displayName property in your component.
About the try/catch?
Try /catch is great but it can only be used for imperative code:
try {
showButton();
} catch (error) {
// ...
}
Copy the code
But the React component is declarative and specifies exactly what is to be rendered:
<Button />
Copy the code
The exception catch boundary retains the React declarative nature and works as you’d expect. Even if an exception occurs by calling the setState method in the componentDidUpdate method deep in the component tree, it passes the exception to the nearest exception capture boundary.
About Event Handlers
Exception capture boundaries do not catch exceptions that occur in event handlers.
React does not require exception capture boundaries to fix exceptions that occur in event handlers. Unlike the Render methods and lifecycle methods, event handlers are no longer called during rendering. So even if the event handler throws an exception, React still knows what to show.
If you need to catch an exception in an event handler, use the regular JavaScript try/catch statement:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { error: null };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {try {// throw an exception} catch (error) {this.setstate ({error}); }}render() {
if (this.state.error) {
return <h1>Caught an error.</h1>
}
return <div onClick={this.handleClick}>Click Me</div>
}
}
Copy the code
Note that the above example shows normal JavaScript behavior and does not use exception catching boundaries.
Naming changes since React15
Act15 has a method with limited support for exception catching: unstable_handleError. This method no longer works, and starting with React16, you need to change the unstable_handleError method to componentDidCatch.
For this change, we provided Codemod to automatically migrate your code.