React 全局错误捕获(类组件和函数组件)

146 阅读1分钟

错误边界捕获

根据需要选择类组件或者函数组件,二选一即可

类组件

import React from 'react';

// 类组件:实现错误边界的核心逻辑
class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            hasError: false,
            error: null,
            errorInfo: null,
        };
    }

    static getDerivedStateFromError(error) {
        // 更新 state,使下一次渲染能够显示备用 UI
        return { hasError: true, error };
    }

    componentDidCatch(error, errorInfo) {
        // 可以在这里将错误信息上报到错误监控服务
        this.setState({
            errorInfo,
        });
    }

    render() {
        if (this.state.hasError) {
            // 渲染降级 UI
            return (
                <div>
                    <h2>Something went wrong.</h2>
                    <details style={{ whiteSpace: 'pre-wrap' }}>
                        {this.state.error.toString()}
                        <br />
                        {this.state.errorInfo?.componentStack}
                    </details>
                </div>
            );
        }

        return this.props.children;
    }
}

export default ErrorBoundary;

函数组件

此处以 tsx 为例,若使用的是 jsx,只需要把类型去掉

import React, { useEffect, useState } from 'react';

// 函数组件:实现错误边界的核心逻辑
type ErrorBoundaryProps = {
    children: React.ReactNode;
};

const ErrorBoundary: React.FC<ErrorBoundaryProps> = ({ children }) => {
    const [hasError, setHasError] = useState<boolean>(false);
    const [error, setError] = useState<Error | null>(null);
    const [errorInfo, setErrorInfo] = useState<React.ErrorInfo | null>(null);

    useEffect(() => {
        // 捕获全局错误
        const errorHandler = (event: PromiseRejectionEvent | ErrorEvent) => {
            setError(event instanceof ErrorEvent ? event.error : event.reason);
            setErrorInfo(event instanceof ErrorEvent ? event.errorInfo : null);
            setHasError(true);
        };
        window.addEventListener('error', errorHandler);
        window.addEventListener('unhandledrejection', errorHandler);

        return () => {
            window.removeEventListener('error', errorHandler);
            window.removeEventListener('unhandledrejection', errorHandler);
        };
    }, []);

    if (hasError) {
        // 渲染降级 UI
        return (
            <div>
                <h2>Something went wrong.</h2>
                <details style={{ whiteSpace: 'pre-wrap' }}>
                    {error.toString()}
                    <br />
                    {errorInfo?.componentStack}
                </details>
            </div>
        );
    }

    return children;
};

export default ErrorBoundary;

主组件

错误边界捕获在主组件中的应用

示例

仅限使用于展示错误边界的捕获效果

import React, { useState } from 'react';
import ErrorBoundary from './ErrorBoundary';

function ChildComponent({ throwError }) {
    if (throwError) {
        throw new Error('Error from ChildComponent!');
    }
    return <div>Child Component is working!</div>;
}

export default function App() {
    const [throwError, setThrowError] = useState(false);

    return (
        <div>
            <h1>React Error Handling</h1>
            <button onClick={() => setThrowError(true)}>Trigger Error</button>

            <ErrorBoundary>
                <ChildComponent throwError={throwError} />
            </ErrorBoundary>
        </div>
    );
}

实际应用

用 ErrorBoundary 将你原本的代码包住,放置最外层

import React, { useState } from 'react';
import ErrorBoundary from './ErrorBoundary';

export default function App() {
    const [throwError, setThrowError] = useState(false);

    return (
        <ErrorBoundary>
             // 你的组件/代码
        </ErrorBoundary>
    );
}