React中的错误边界(Error Boundary)

1,662 阅读3分钟

一、什么是错误边界

默认情况下,若一个组件在渲染期间(render) 发生错误,会导致整个组件树全部被卸载

错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误

二、错误边界的注意点⚠️

错误边界无法捕获一下场景中出现的错误:

  1. 自身的错误
  1. 异步的错误
  1. 事件中的错误
  1. 服务端渲染的错误

总结:仅处理渲染子组件期间的同步错误

三、如何写出一个错误边界组件

错误边界的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。

附:React官方的例子

如果一个 class组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界

1. 编写生命周期函数 getDerivedStateFromError

  1. 静态函数

  2. 运行时间点:渲染子组件的过程中,发生错误之后,在更新页面之前

  3. 注意:只有子组件发生错误,才会运行该函数

  4. 该函数返回一个对象,React会将该对象的属性覆盖掉当前组件的state

  5. 参数:错误对象

  6. 通常,该函数用于改变状态

2. 编写生命周期函数 componentDidCatch

  1. 实例方法

  2. 运行时间点:渲染子组件的过程中,发生错误,更新页面之后,由于其运行时间点比较靠后,因此不太会在该函数中改变状态

  3. 通常,该函数用于记录错误消息

import React, { PureComponent } from  react ;
import ErrorPage from  ../ErrorPage ;

export default class ErrorBound extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
    };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 1、错误信息(error)
    // 2、错误堆栈(errorInfo)
    console.log(error, errorInfo);
  }

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

注意: 如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 catch {} 的工作机制

四、错误边界的作用范围

虽然函数式组件无法定义 Error Boundary,但 Error Boundary 可以捕获函数式组件的异常错误:

// ErrorBoundary 组件
class ErrorBound extends React.Component {
  // ...
}

// Hooks 函数组件
const Child = (props) => {
  React.useEffect(() => {
    console.log(1);
    props.a.b;
    console.log(2);
  }, [props.a.b]);

  return <div />;
};

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

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

const Child = (props) => {
  React.useEffect(() => {
    console.log(1);
    props.a.b;
    console.log(2);
  }, []);
  return <div />;
};

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

五、ErrorPage设计

以H5为例:

六、接入移动端框架模板

import ErrorBoundary from '@homed/ErrorBoundary';

const handleSlardarReport = useCallback((error: Error, errorInfo: React.ErrorInfo) => {
    slardarInstance.fatal(` [ErrorBoundary Render Error]: ${error.message} | ${errorInfo.componentStack}`);
}, []);

const fallbackUI = () => (
  <div>
    <div>渲染失败,兜底组件</div>
  </div>
);

return (
    <ErrorBoundary fallbackRender={fallbackUI} onError={handleSlardarReport} trigger>
      <LogProvider value={{ ...commonParams(), cur_page }}>
        <Layout {...props} />
      </LogProvider>
    </ErrorBoundary>
);

参考文档

错误边界 – React

微信关注公众号《小前日记 》获取源码

IMG_5155.JPG