148. 精读《React Error Boundaries》

2022-03-14 17:37:15 浏览数 (1)

1 引言

Error Boundaries 是 React16 提出来用来捕获渲染时错误的概念,今天我们一起读一读 A Simple Guide to Error Boundaries in React 这篇文章,了解一下这个重要机制。

2 概述

Error Boundaries 可以用来捕获渲染时错误,API 如下:

代码语言:javascript复制
class MyErrorBoundary extends Component {
  state = {
    error: null,
  };

  static getDerivedStateFromError(error) {
    // 更新 state,下次渲染可以展示错误相关的 UI
    return { error: error };
  }

  componentDidCatch(error, info) {
    // 错误上报
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.error) {
      // 渲染出错时的 UI
      return <p>Something broke</p>;
    }
    return this.props.children;
  }
}
  • static getDerivedStateFromError: 在出错后有机会修改 state 触发最后一次错误 fallback 的渲染。
  • componentDidCatch: 用于出错时副作用代码,比如错误上报等。

这两种方法中任意一个被定义时,这个组件就会成为 Error Boundary 组件,可以阻止子组件渲染时报错。

最后作者还提出一个建议,建议将 Error Boundary 单独作为一个组件,而不是将错误监听方法与业务组件耦合,一方面考虑到复用,另一方面则因为错误检测只对子组件生效。

好吧,其实 React 官方文档比这篇文章介绍的详细的多得多,原文介绍到此结束。

3 精读

React Error Boundaries 官方文档 里提到了四种无法 Catch 的错误场景:

  1. 回调事件。由于回调事件执行时机不在渲染周期内,因此无法被 Error Boundary Catch 住,如有必要得自行 try/catch。
  2. 异步。比如 setTimeoutrequestAnimationFrame,和第一条同理。
  3. 服务端渲染。
  4. Error Boundary 组件自身触发的错误。因为只能捕获其子组件的错误。

这也是使用 Error Boundaries 最容易有疑问的地方。除了上面的情况,笔者结合自身经验再列举几种异常边界场景。

无法捕获编译时错误

很明显,即便是 React 官方 API Error Boundary 也只能捕获运行时错误,而对编译时错误无能为力。

编译时错误包括不限于编译环境错误、运行前的框架错误检查提示、TS/Flow 类型错误等,这些都是 Error Boundary 无法捕获的,而且没有更好的办法 Catch 住,遇到编译错误就在编译时解决吧,仅关注运行时错误就好了。

可以作用于 Function Component

虽然函数式组件无法定义 Error Boundary,但 Error Boundary 可以捕获函数式组件的错误,因此可以曲线救国:

代码语言:javascript复制
// ErrorBoundary 组件
class ErrorBoundary extends React.Component {
  // ...
}

// 可以捕获所有组件异常,包括 Function Component 的子组件
const App = () => {
  return (
    <ErrorBoundary>
      <Child />
    </ErrorBoundary>
  );
};

对 Hooks 也可生效

对于 Hooks 中异常也可以生效,比如下面的代码:

代码语言:javascript复制
const Child = (props) => {
  React.useEffect(() => {
    console.log(1);
    props.a.b;
    console.log(2);
  }, [props.a.b]);

  return <div />;
};

要注意的是,出现在 deps 中的错误会立即被 Catch,导致 console.log(1) 都无法打印。但如果是下面的代码,则可以打印出 console.log(1),无法打印出 console.log(2):

代码语言:javascript复制
const Child = (props) => {
  React.useEffect(() => {
    console.log(1);
    props.a.b;
    console.log(2);
  }, []);

  return <div />;
};

所以 React 官网的这句话并不是指 Error Boundary 对 Hooks 不生效,而是指 Error Boundary 无法以 Hooks 方式指定,对功能是没有影响的:

componentDidCatch and getDerivedStateFromError: There are no Hook equivalents for these methods yet, but they will be added soon.

所以这里的理解要注意一下,另外 React 官方文档 Hooks FQA 有很多宝藏,建议抽时间逐条阅读。

4 总结

Error Boundary 可以捕获所有子元素渲染时异常,包括 render、各生命周期函数,但也有很多使用限制,希望你可以正确使用它。

错误捕获也不是万能的,更多时候我们要避免并及时修复错误,通过错误捕获降低出错时对用户体验的影响,并在第一时间内监控起来并快速修复。

最后,你有明明正确使用了 Error Boundary 却依然无法 Catch 住的错误 Case 吗?

讨论地址是:精读《React Error Boundaries》 · Issue #246 · dt-fe/weekly

版权声明:自由转载-非商用-非衍生-保持署名(创意共享 3.0 许可证)

0 人点赞