React Error Boundaries
过去,如果我们在组件内部遇到任何JavaScript错误,它将破坏React的内部状态,并在下一次渲染时将React置于损坏的状态。在React组件中没有办法处理这些错误,也没有提供任何从它们中恢复的方法。但是,React16 引入了一个新概念,即可以使用错误边界处理错误。现在,如果在用户界面的一部分中发现任何JavaScript错误,则不会破坏整个应用程序。
错误边界是React组件,可在我们应用程序中的任何位置捕获JavaScript错误,记录这些错误并显示后备用户界面。它不会破坏整个应用程序组件树,仅在组件发生错误时才呈现后备UI。错误边界会在组件生命周期方法及其下的整个树的构造函数的渲染过程中捕获错误。
注意:
有时,无法在React应用程序中捕获错误边界。这些是:
事件处理程序
异步代码(例如setTimeout或requestAnimationFrame回调)
服务器端渲染
错误抛出在错误边界本身而不是其子项中。
对于简单的React应用程序,我们可以一次声明一个错误边界,并将其用于整个应用程序。对于具有多个组件的复杂应用程序,我们可以声明多个错误边界以恢复整个应用程序的每个部分。
我们还可以将错误报告给错误监视服务,例如 Rollbar >。此监视服务提供了跟踪有多少用户受到错误影响,查找错误原因并改善用户体验的功能。
类中的错误边界
如果类组件定义了新的生命周期方法(静态getDerivedStateFromError()或componentDidCatch(error,info)),则可能成为错误边界。当引发错误时,我们可以使用静态getDerivedStateFromError()呈现后备UI,并且可以使用componentDidCatch()来记录错误信息。
错误边界无法在自身内部捕获错误。如果错误边界未能呈现错误消息,则错误将转到其上方最近的错误边界。它类似于JavaScript中的catch {}块。
如何实现错误边界
Step-1 扩展React组件并在其中传递Props。
Step-2 现在,添加componentDidCatch()方法,该方法可让您捕获树中它们下面的组件中的错误。
步骤3 接下来,添加render()方法,该方法负责如何呈现组件。例如,它将显示错误消息,例如"出现了错误。"
示例
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this .state = { hasError: false };
}
static getDerivedStateFromError(error) {
// It will update the state so the next render shows the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
// It will catch error in any component below. We can also log the error to an error reporting service.
logErrorToMyService(error, info);
}
render() {
if (this .state.hasError) {
return (
<div>Something is wrong.</div>;
);
}
return this .props.children;
}
}
第4步现在,我们可以将其用作常规组件。在HTML中添加要包含在错误边界中的新组件。在此示例中,我们在MyWidgetCounter组件周围添加了错误边界。
<ErrorBoundary>
<MyWidgetCounter/>
</ErrorBoundary>
在哪里放置错误边界
错误边界完全取决于您。您可以在应用程序组件的顶层使用错误边界,也可以将其包装在各个组件上,以防止它们破坏应用程序的其他部分。
让我们看一个例子。
import React from 'react' ;
import './App.css'
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this .state = { error: false , errorInfo: null };
}
componentDidCatch(error, errorInfo) {
// catch errors in any components below and re-render with error message
this .setState ({
error: error,
errorInfo: errorInfo
})
}
render() {
if (this .state.errorInfo) {
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this .state.error && this .state.error.toString ()}
<br />
{this .state.errorInfo.componentStack}
</details>
</div>
);
}
return this .props.children;
}
}
class BuggyCounter extends React.Component {
constructor(props) {
super(props);
this .state = { counter: 0 };
this .handleClick = this .handleClick.bind (this );
}
handleClick() {
this .setState (({counter}) => ({
counter: counter + 1
}));
}
render() {
if (this .state.counter === 3) {
throw new Error('I crashed!' );
}
return <h1 onClick={this .handleClick}>{this .state.counter}</h1>;
}
}
function App() {
return (
<div>
<p><b>Example of Error Boundaries</b></p>
<hr />
<ErrorBoundary>
<p>These two counters are inside the same error boundary.</p>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
<hr />
<p>These two counters are inside of their individual error boundary.</p>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
</div>
);
}
export default App
在上面的代码段中,当我们单击数字时,它会增加计数器。计数器被编程为在达到 3 时抛出错误。它模拟组件中的JavaScript错误。在这里,我们以两种方式使用错误边界,如下所示。
首先,这两个计数器在同一错误边界内。如果有人崩溃,错误边界将替换掉它们。
<ErrorBoundary>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
第二,这两个计数器在其各自的错误边界内。因此,如果任何人崩溃,其他人都不会受到影响。
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
输出:
执行上述代码时,将获得以下输出。
当计数器达到3时,将提供以下输出。
未捕获错误的新行为
这是与错误边界相关的重要含义。如果错误没有被错误边界捕获,将导致整个React应用程序的 unmounting 。
事件处理程序中的错误边界
错误边界不允许在事件处理程序中捕获错误。 React不需要任何错误边界即可从事件处理程序中的错误中恢复。如果需要在事件处理程序中捕获错误,则可以使用JavaScript try-catch 语句。
在下面的示例中,您可以看到事件处理程序将如何处理错误。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this .state = { error: null };
this .handleClick = this .handleClick.bind (this );
}
handleClick() {
try {
// do something which can throw error
} catch (error) {
this .setState ({ error });
}
}
render() {
if (this .state.error) {
return
<h2>It caught an error.</h2>
}
return <div onClick={this .handleClick}>Click Me</div>
}
}