上一章ReactJS实战教程请查看:React Portal(插槽)用法全解
在过去,如果我们在组件中得到任何JavaScript错误,它会破坏React的内部状态,在接下来的渲染React状态将会比较难处理。React组件中没有处理这些错误的方法,也没有提供任何从中恢复的方法。但是,React 16引入了一个新概念,通过使用错误边界来处理错误。现在,如果在部分UI中发现任何JavaScript错误,它不会破坏整个应用程序。
错误边界是React组件,它捕捉应用程序中任何地方的JavaScript错误,记录这些错误,并显示备用UI。它不会破坏整个app组件树,只会在组件发生错误时呈现回退UI。错误边界捕获组件生命周期方法中呈现期间的错误,以及它们下面整个树的构造函数。
注意:
有时,在React应用程序中捕获错误边界是不可能的。这些都是:
- 事件处理程序
- 异步代码(例如setTimeout或requestAnimationFrame回调)
- 服务器端渲染
- 错误将在错误边界中抛出,而不是在其子边界中抛出。
对于简单的React应用程序,我们可以一次性声明一个错误边界,并将其用于整个应用程序。对于具有多个组件的复杂应用程序,我们可以声明多个错误边界来恢复整个应用程序的每个部分。
我们还可以向类似Rollbar的错误监视服务报告错误,此监视服务提供了跟踪有多少用户受到错误影响、查找错误原因并改进用户体验的能力。
类内的错误边界
如果类组件定义了一个新的生命周期方法(静态getDerivedStateFromError()或componentDidCatch(error, info)),它就可能成为一个错误边界。我们可以使用静态的getDerivedStateFromError()在抛出错误时呈现回退UI,还可以使用componentDidCatch()来记录错误信息。
错误边界无法捕获其内部的错误。如果错误边界未能呈现错误消息,则错误将转到其上最近的错误边界。它类似于JavaScript中的catch{}块。
如何实现错误边界
步骤1: 创建一个扩展React组件并在其类中传递props。
步骤2: 现在,添加componentDidCatch()方法,它允许你捕获树中它们下面的组件中的错误。
步骤3: 接下来添加render()方法,该方法负责如何呈现组件。例如,它将显示“Something is wrong”这样的错误消息。
例子
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 它将更新状态,以便在下一个呈现中显示回退UI
return { hasError: true };
}
componentDidCatch(error, info) {
// 它将捕获错误的任何组件如下。我们还可以将错误记录到错误报告服务中
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) {
// 捕获下面任何组件中的错误,并使用错误消息重新渲染
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>错误边界的例子</b></p>
<hr />
<ErrorBoundary>
<p>这两个计数器位于相同的错误边界内</p>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
<hr />
<p>这两个计数器位于各自的错误边界内</p>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
</div>
);
}
export default App
在上面的代码片段中,当我们单击数字时,它会增加计数器。当计数器达到3时,程序会抛出错误,它模拟组件中的JavaScript错误。在这里,我们以两种方式使用误差边界,如下所示。
首先,这两个计数器位于相同的错误边界内,如果有人崩溃,错误边界将同时替换他们。
<ErrorBoundary>
<BuggyCounter />
<BuggyCounter />
</ErrorBoundary>
其次,这两个计数器位于各自的错误边界内。因此,如果有人崩溃,另一个就不会受到影响。
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
<ErrorBoundary><BuggyCounter /></ErrorBoundary>
未捕获错误的新行为
这是与误差边界相关的一个重要含义。如果错误没有被任何错误边界捕获,将导致整个React应用程序的卸载。
事件处理程序中的错误边界
错误边界不允许在事件处理程序中捕获错误。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 {
// 做一些会抛出错误的操作
} catch (error) {
this.setState({ error });
}
}
render() {
if (this.state.error) {
return
<h2>它捕获了一个错误.</h2>
}
return <div onClick={this.handleClick}>点击</div>
}
}
评论前必须登录!
注册