zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

React用错误边界来捕获和处理异常错误!

2023-03-20 14:52:03 时间
一、解决什么问题?

UI 中 JavaScript 错误不应该导致整个应用崩溃,错误边界就是解决方案(React 16 增加的功能)。

二、有哪些特性?
1、定义
  • 可捕获子组件 JavaScript 错误,打印错误并展示备用UI的clas组件。
2、无法捕获的错误:
  • 事件处理
  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调函数)
  • 服务端渲染
  • 它自身抛出来的错误(并非它的子组件)
3、怎么写?

错误边界是包含下面任意一个或两个方法 的class组件:

  • static getDerivedStateFromError() :渲染备用 UI
  • componentDidCatch() :打印错误信息
//  错误边界  实例
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;
    }
}
// 使用 错误边界
<ErrorBoundary>
    <MyWidget />
</ErrorBoundary>
4、js代码还是用try / catch 捕获错误
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = { error: null };
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        try {
            // 执行操作,如有错误则会抛出
        } catch (error) {
            this.setState({ error });
        }
    }

    render() {
        if (this.state.error) {
            return <h1>Caught an error.</h1>
        }
        return <button onClick={this.handleClick}>Click Me</button>
    }
}
三、实例展示

一个使用 错误边界 的示例,新建并复制下面代码到index.html文件,浏览器打开index.html即可看到效果。

<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <!-- 第一步:加载开发版本的React -->
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>

    <!-- 部署时,请用下面链接替代上面 -->
    <!-- <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script> -->

    <!-- 第二步:加载开发版本的babel -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

</head>

<body>
    <div id="root"></div>
    <script type="text/babel">
        class ErrorBoundary extends React.Component {
            constructor(props) {
                super(props);
                this.state = { error: null, errorInfo: null };
            }

            componentDidCatch(error, errorInfo) {
                // Catch errors in any components below and re-render with error message
                this.setState({
                    error: error,
                    errorInfo: errorInfo
                })
                // You can also log error messages to an error reporting service here
            }

            render() {
                if (this.state.errorInfo) {
                    // Error path
                    return (
                        <div>
                            <h2>Something went wrong.</h2>
                            <details style={{ whiteSpace: 'pre-wrap' }}>
                                {this.state.error && this.state.error.toString()}
                                <br />
                                {this.state.errorInfo.componentStack}
                            </details>
                        </div>
                    );
                }
                // Normally, just render children
                return this.props.children;
            }
        }

        class BuggyCounter extends React.Component {
            constructor(props) {
                super(props);
                this.state = { counter: 0 };
                this.handleClick = this.handleClick.bind(this);
            }

            handleClick() {
                this.setState(({ counter }) => ({
                    counter: counter + 1
                }));
            }

            render() {
                if (this.state.counter === 5) {
                    // Simulate a JS error
                    throw new Error('I crashed!');
                }
                return <h1 onClick={this.handleClick}>{this.state.counter}</h1>;
            }
        }

        function Err() {
            return (
                <div>
                    <p>
                        <b>
                            This is an example of error boundaries in React 16.
                            <br /><br />
                            Click on the numbers to increase the counters.
                            <br />
                            The counter is programmed to throw when it reaches 5. This simulates a JavaScript error in a component.
                        </b>
                    </p>
                    <hr />
                    <ErrorBoundary>
                        <p>These two counters are inside the same error boundary. If one crashes, the error boundary will replace both of them.</p>
                        <BuggyCounter />
                        <BuggyCounter />
                    </ErrorBoundary>
                    <hr />
                    <p>These two counters are each inside of their own error boundary. So if one crashes, the other is not affected.</p>
                    <ErrorBoundary><BuggyCounter /></ErrorBoundary>
                    <ErrorBoundary><BuggyCounter /></ErrorBoundary>
                </div>
            );
        }

        ReactDOM.render(
            <Err />,
            document.getElementById('root')
        );

    </script>
</body>
</html>
四、参考文档: