zoukankan      html  css  js  c++  java
  • React 错误边界组件

    这是React16的内容,并不是最新的技术,但是用很少被讨论,直到通过文档发现其实也是很有用的一部分内容,还是总结一下~

    React中的未捕获的 JS 错误会导致整个应用的崩溃,和整个组件树的卸载。从 React16 开始就是这样。但是同时React也引入了一个新的概念——错误边界。

    定义,是什么

    错误边界仍然是一种组件,可以捕获(打印或者其他方式)处理该组件的子组件树任何位置的 JavaScript 错误,并根据需要渲染出备用UI.

    工作方式类似于try-catch,但是错误边界只用于 React 组件。

    只有class组件能够成为错误边界组件。错误边界仅可以捕获子组件的错误,无法捕获自身的错误

    错误边界会在渲染期间,生命周期和整个组件树的构造函数中捕获错误。如果没有错误边界处理,渲染的还是崩溃的子组件树,这显然不是我们想要的。

    通过一个例子来逐步演示要怎么用错误边界:

    export default class ErrorTest extends Component {
      constructor(props) {
        super(props);
      }
      render() {
        return (
          <div>
            <BugCounter></BugCounter>
            <span>my name is dan</span>
          </div>
        );
      }
    }
    
    // Bug 报错组件
    class BugCounter extends Component {
      constructor(props) {
        super(props);
        this.state = {
          counter: 0,
        };
      }
      click = () => {
        this.setState(({ counter }) => ({ counter: counter + 1 }));
      };
      render() {
        if (this.state.counter === 5) {
          throw new Error("crashed!");
        }
        return (
          <div>
            <h3 onClick={this.click}>{this.state.counter}</h3>
          </div>
        );
      }
    }
    

    上面代码的渲染结果(忽略样式):

    点击数字0,会逐步递增。但是数字等于5的时候,组件会抛出一个Error:

    Error会引起整个Demo的崩溃,连外部的<span>my name is dan</span>也显示不出来了,这时还没有添加错误边界。

    生产模式下,会直接白屏,并在控制台报错:

    getDerivedStateFromError & componentDidCatch

    需要一个错误边界来处理这种崩溃。如何定义一个错误边界?

    定义一个组件,并实现static getDerivedStateFromError() 或者componentDidCatch() 生命周期方法(可以都实现或者选择其一)。这个组件就会变成一个错误边界

    关于这两个生命周期函数,可以通过链接查看,总结如下:

    componentDidCatch(error, info)
    

    error是抛出的错误对象,而info则包含了组件引发错误的栈信息。函数在提交阶段被调用。是可以执行副作用的。

    static getDerivedStateFromError(error)
    

    在子组件抛出错误后调用,会将抛出的错误作为参数。需要返回一个值,以更新state。该函数在渲染阶段调用,不允许出现副作用。如果在捕获错误后需要执行副作用操作,应该在componentDidCatch中进行。

    制作错误边界组件

    可以使用组合的方式,在要使用的组件上面添加一个错误边界组件包裹一层。该组件需要这些效果:

    • 捕获子组件错误,组件内部记录出错状态
    • 在出错状态下显示备用UI,在正常状态下显示子组件

    那么就可以像这样:

    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        // 更新 state 使下一次渲染能够显示降级后的 UI
        return { hasError: true };
      }
    
      componentDidCatch(error, errorInfo) {
        // 你同样可以将错误日志上报给服务器
        logErrorToMyService(error, errorInfo);
      }
    
      render() {
        if (this.state.hasError) {
          // 你可以自定义降级后的 UI 并渲染
          return <h1>Something went wrong.</h1>;
        }
    
        return this.props.children; 
      }
    }
    

    捕获到错误之后的副作用是自定义的,上传服务器,或者用state记录再显示在页面上:

    componentDidCatch(error, errorInfo) {
      // Catch errors in any components below and re-render with error message
      this.setState({
        error: error,
        errorInfo: errorInfo
      })
    }
    

    捕获处理

    加上所有代码,将有问题的组件用错误边界的组件包裹起来,看看结果:

    import { Component } from "react";
    
    export default class ErrorTest extends Component {
      render() {
        return (
          <div>
            <ErrorBoundary>
              <BugCounter></BugCounter>
            </ErrorBoundary>
            <span>my name is dan</span>
          </div>
        );
      }
    }
    
    // Bug 报错组件
    class BugCounter extends Component {
      constructor(props) {
        super(props);
        this.state = {
          counter: 0,
        };
      }
      click = () => {
        this.setState(({ counter }) => ({ counter: counter + 1 }));
      };
      render() {
        if (this.state.counter === 5) {
          throw new Error("crashed!");
        }
        return (
          <div>
            <h3 onClick={this.click}>{this.state.counter}</h3>
          </div>
        );
      }
    }
    
    // 错误边界处理组件
    class ErrorBoundary extends Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        // 更新 state 使下一次渲染能够显示降级后的 UI
        return { hasError: true };
      }
    
      render() {
        if (this.state.hasError) {
          // 你可以自定义降级后的 UI 并渲染
          return <h1>Something went wrong.</h1>;
        }
    
        return this.props.children;
      }
    }
    
    

    抛出异常在开发模式下依然是报错的,但是在使用yarn build之后,再通过http-server挂起来之后,访问生产的页面:

    可以看到,虽然因为throw error控制台出错,但是my name is dan的显示并没有被影响,也就是说,错误边界内部的子组件错误没有影响到外部其他组件和元素。

    作用范围

    错误边界用于处理子组件生命周期和渲染函数上的错误,对于事件处理器,不会在渲染期间触发,对于事件处理器抛出的异常应该用try catch

    错误边界无法捕获这些场景中的错误:

    • 事件处理
    • 异步代码
    • 服务端渲染
    • 错误边界自身抛出的错误(非子组件)

    关于错误边界,一个 React的官方demo值得尝试:

    https://codepen.io/gaearon/pen/wqvxGa?editors=0010


    参考:

  • 相关阅读:
    浅谈HTTP中Get与Post的区别
    js 执行完setTimeout再接着执行函数
    2017年书单
    js判断img是否存在
    md5
    GIF播放器
    java 集合(二)
    java 泛型
    抓包工具
    js计算地球两个经纬度之间的距离
  • 原文地址:https://www.cnblogs.com/xuxiaowei/p/14645136.html
Copyright © 2011-2022 走看看