凯小凯

React16中的错误处理 - React Blog

ReactDocsTutorialCommunityBlogv16.0.0-rc.3GitHub

React16的错误处理

2017.9.26 由Dan Abramov撰写。

随着React16的发布越来越接近,我们想宣布一些关于在组件内如何处理JavaScript错误的变化。这些变化包括在React16 Beta版本,并将会成为React16的一部分。

顺便说一句, 我们刚刚发布了第一个React16 beta让你尝试!

React15和更早版本中的行为

在过去,组件内部的JavaScript错误会破坏React的内部状态,并导致它在下一步的渲染中触发神秘错误 。这些错误经常是由代码中早期的错误引起的,但是React并没有提供一种在组件中优雅地处理它们的方法,并且无法从它们中恢复过来。

引入错误边界

UI部分的一个JavaScript错误不应该破坏整个程序。为了给React用户解决这个问题,React16引入了“错误边界”的新概念。

错误边界是在他们的子组件树中捕捉JavaScript错误,记录这些错误,并显示一个回退UI的React组件,而不是崩溃的组件树。错误边界捕捉渲染过程中、生命周期方法中以及它们下面整个树的构造函数中的错误。

如果一类组件定义了一个新的生命周期方法componentDidCatch(error, info),那么这类组件就成为一个错误边界:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // 显示回退UI
    this.setState({ hasError: true });
    // 你也可以把错误信息上报给错误上报服务器
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // 你可以渲染任何自定义的回退UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

然后就可以作为常规组件使用它:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

这个componentDidCatch()方法就像JavaScript中的 catch {} 块,但它是应用于组件上的。只有组件类可以成为错误边界。实际上,大多数情况下您希望声明一次错误边界组件,并在整个应用程序中使用它。

注意,错误边界只能捕获树结构中它下面组件中的错误。一个错误边界不能捕获它本身的错误。如果错误边界捕获错误失败,则错误将传播到上面最接近的错误边界。这也类似于JavaScript中catch {}块的工作原理。

在线演示

查看这个声明的例子和使用错误边界React 16 beta一起。

在哪里放置错误边界

错误边界的粒度取决于您。您可以包装顶层路由组件来向用户显示“出错”消息,就像服务器端框架经常处理崩溃一样。您还可以将单个小组件封装在错误边界中,以保护它们不致破坏应用程序的其余部分。

针对未捕获错误的新行为

这一变化具有重要意义。对于React16,没有被任何错误边界捕获的错误将导致整个React组件树的卸载。

我们讨论了这个决定,但根据我们的经验,把损坏的UI留下比彻底删除更糟糕。例如,在像Messenger这样的产品中,留下破损的UI可能导致某人向错误的人发送消息。同样,对于一个支付应用程序显示错误的金额比什么都不渲染要坏。

这种变化意味着,当您迁移到React16时,您可能会发现以前应用程序中没有注意到的错误崩溃。添加错误边界,可以在出错时,提供更好的用户体验。

例如,Facebook Messenger将边栏、信息面板、会话日志和消息输入的内容封装到不同的错误边界中。如果某个UI区域中的某个组件崩溃,剩下的部分仍然保持交互。

我们也鼓励您使用JS错误上报服务(或建立您自己的),您可以了解他们在生产中发生的未处理的异常,并修复。

组件的堆栈跟踪

在开发过程中,React16会将渲染过程中发生的所有错误打印到控制台,即使应用程序意外地将它们删除。除了错误消息和JavaScript的栈,它也提供了组件的堆栈跟踪。现在你可以精确地看到在组件树的哪部分发生了错误:

Component stack traces in error message

你也可以看到文件名和行号在组件堆栈跟踪中。这在Create React App脚手架中是默认的:

Component stack traces with line numbers in error message

如果你不使用Create React App,你可以添加这个插件手动修改你的Babel配置。请注意,它只是为了在开发过程中使用,在生产环境一定要禁止

为什么不用try / catch?

try / catch很伟大,但是它只适用于必要的代码:

try {
  showButton();
} catch (error) {
  // ...
}

然而,React组件是声明和指定什么内容应该呈现:

`<Button />`

错误边界保留了React的声明性,并按您预期的方式运行。例如,即使一个错误发生在componentDidUpdate,但是它是由组件树深处的某个setState造成的,它仍然会正确地传播到最近的错误边界。

从React15命名更改

React15包含一个对错误边界支持很有限的方法,它有一个不同的名字:unstable_handleError。这种方法不再工作,从最初的16 beta版本开始,您需要在代码中把它改为componentDidCatch

对于这种变化,我们提供了a codemod来自动迁移您的代码。

编辑这篇文章

最近的文章

React16错误处理相关文章:Quick StartThinking in ReactTutorialAdvanced GuidesCommunityStack OverflowDiscussion ForumReactiflux ChatFacebookTwitterResourcesConferencesVideosExamplesComplementary ToolsMoreBlogGitHubReact NativeAcknowledgements

Facebook Open Source

Copyright © 2017 Facebook Inc.