React项目优化(4)-使用错误边界(ErrorBoundry)解决白屏问题

1,603 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

上一篇:React项目优化(3)-按钮权限组件优化


错误边界(ErrorBoundry)介绍

我们知道在React项目中,如果某个React组件报错,但是这个错误没有被捕获就会导致整个React组件树被卸载,进而导致整个页面显示为白屏。 如果在项目使用中出现这种情况,客户体验感太差,开发人员跟踪问题也不方便。我们势必要解决此问题。


在React16 中,官方就这个问题,提出了“错误边界”这个概念,用于专门处理此问题。

原理: 利用componentDidCatch这个声明周期,在render() 函数抛出错误时,这个声明周期函数就可以捕捉到错误信息,然后我们将信息抛出,利于开发者迅速定位并处理问题。

全局级别的错误捕获

通过定制ErrorBoundry 组件,我们将业务逻辑组件包裹在此组件内,通过componentDidCatch钩子来获取错误信息,同时在Render方法中渲染报错后需要显示内容,这里可以根据需求,个性化定制样式。

组件定义

// 代码示例
class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { error: null, errorInfo: null };
  }
  // 捕获事件,这是核心问题.
  componentDidCatch(error, errorInfo) {
    this.setState({
      error: error,
      errorInfo: errorInfo,
    });
  }
  render() {
    let { error, errorInfo } = this.state;
    let { msgInfo } = this.props;
    if (error) {
      let { message } = error;
      if (message) {
        // Error(message);
      }
      if (msgInfo) {
        return <div className="error-info-wrap">{msgInfo}</div>;
      } else {
        return <ErrorTemplate error={error} />;
      }
    }
    // 正常页面,渲染子组件
    return this.props.children;
  }
}

组件使用

import ErrorBoundary from "components/ErrorBoundary";
render() {
    return (
      <ErrorBoundary>
        <div className="route-content">
          <Route exact path="/" component={ListContainer} />
          <Route exact path="/edit" component={EditContainer} />
        </div>
      </ErrorBoundary>
    );
}

为了保险起见,我们将该组件套在渲染Route的外面,这样在该单页面应用中,不论那层组件报错,我都可以顺利捕获。同时在页面和控制台输出报错信息。

组件级别的错误捕获

另外:为了能更精确的捕获错误发生的组件,也为了尽做大可能显示页面信息。 换句话说,为了保证页面上除了有错误的组件外其他组件的正常渲染。

我们在几个常用的核心组件中嵌入了刚才的ErroryBoundry组件。

如下:

// 这是表单核心组件
  <ErrorBoundary msgInfo="表单字段报错!">
    <Row className="pro-field-group">
      {filedGroupDom}
      {React.Children.count(children) > 0 &&
        React.Children.map(children, (thisArg) => (
          <Col {...curCol}>{thisArg}</Col>
        ))}
    </Row>
  </ErrorBoundary>
// 这是表格核心组件
<ErrorBoundary msgInfo="editTable 组件错误!">
    <Grid columns={gridColums} 
          paginationObj={{ verticalPosition: "none" }} 
          columnFilterAble={rowEditStatus} 
          showHeaderMenu={rowEditStatus} 
          dragborder={rowEditStatus} 
          draggable={rowEditStatus} 
          syncHover={rowEditStatus} 
          {...otherProps} 
     />
</ErrorBoundary>

注意点

  1. 在实际开发中,开发者只需要在根组件引入错误边界组件。不需要处理表格组件、表单组件中的错误边界。
  2. ErroryBoundary仅仅可以捕获到被其包裹的组件的错误,而不能捕获到本身级别的错误,所以我把它放在路由节点的外面。
  3. 它很像JS的catch{}模块一样,但是对于组件,只有class类型的组件可以使用它。
  4. 它不能捕获异步代码,所以针对于异步请求的捕获,我们使用了try/catch处理。
 try {
    const res = processData(
      await commonRequest("verifyReport", { id: searchId })
    );
    if (res) {
      const { result } = res;
      const { success, data } = result;
      if (success) {
         // do sth...
      }
    }
 } catch (error) {
     Error(error);
 }