【问题标题】:React ErrorBoundary - Just can't get it to workReact ErrorBoundary - 只是无法让它工作
【发布时间】:2019-07-17 11:22:30
【问题描述】:

抱歉,我知道有人问过这个问题,但我找不到一个适合我的示例。似乎很难理解是否有这么多人在为此苦苦挣扎。

我只需要知道如何在 React 中以一种干净、简单的方式捕获错误。我正在使用 CRA 2.0/React 16.7。我想要一个动作级别的 try/catch 块,因为这是业务逻辑集中在我的应用程序中的地方。

我阅读并按照描述实现了它,但我的 ErrorBoundary 对象从未捕获错误。

例子:

import React, { Component } from "react";

class ErrorBoundary extends Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }

    static getDerivedStateFromError(error) {
        return { hasError: true };
    }

    componentDidCatch(error, info) {
        console.log("ErrorBoundary: ", error);
        console.log("ErrorBoundary: ", info);
    }

    render() {
        if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
        }
        return this.props.children;
    }
}

export default ErrorBoundary;

我在顶层包装我的路线:

    return (
        <BrowserRouter>
            <ErrorBoundary>
                <div className="main-container">
                    <SideBar />
                    <div className="some-class">
                        <Switch>
                            <Route path="/events" component={Events} />
                            <Route exact path="/" component={Index} />
                        </Switch>
                    </div>
                </div>
            </ErrorBoundary>
        </BrowserRouter>
    );

在上面的“事件”中,我进行了一个 API 调用,它在 API 端抛出了 500 错误:

componentDidMount() {
    this.props.getAllEvents();
}

这是 Redux 操作:

export const getAllEvents = () => {
    return async (dispatch, getState) => {
        try {
            let state = getState();
            const result = await get({
                state: state,
                route: "/v1/events"
            });
            dispatch({
                type: GET_EVENTS,
                payload: result
            });
        } catch (e) {
            console.log("Something went wrong at action...");
        }
    };
};

..."get()" 只是包装了一个 Axios GET - 没什么特别的。

我在控制台中看到来自失败的 API 调用的 500 错误。从上面的 catch 块中,我从未在控制台中看到“出现问题...”,尽管在调试时该行确实被击中。

“componentDidCatch()”方法永远不会被调用——“hasError”总是假的,它总是渲染子元素。

如果我删除 API 端点中的 throw 块,一切正常,并且我得到了我的数据。我只是无法在 UI 级别捕获错误。我在“componentDidMount()”中尝试了 try/catch,我尝试在操作中删除 try/catch 块...行为没有改变。

提前致谢。

【问题讨论】:

    标签: javascript reactjs redux error-handling


    【解决方案1】:

    正如另一个答案正确说明的那样,React 错误边界只会捕获渲染错误。

    由于您使用的是 redux,因此您可以为像您这样的情况构建自己的机制:

    1. 无论何时发生错误,您都会使用error 属性调度一个操作。
    2. reducer 只查找具有error 属性的操作。它接收操作并在您的状态树中更新“全局错误”状态
    3. 应用范围内的 connected 包装器组件会看到此状态更改并显示回退 UI。

    例如:
    使用error 属性调度操作:

    export const getAllEvents = () => {
        return async (dispatch, getState) => {
            try {
                ...
            } catch (e) {
                dispatch({
                    type: ERROR, 
                    error: e // dispatch an action that has `error` property
                });
            }
        };
    };
    

    reducer 看到它并更新 state.error 部分:

    export default function errorReducer(state = null, action) {
      const { type, error } = action;
      if (type === RESET_ERROR_MESSAGE) {  // an action to clear the error
        return null;
      } else if (error) { // any type of action, but contains an `error`
        return error;
      }
      return state;
    }
    

    现在您将应用包装在 connected 边界中:

    function Wrapper({error}) {
      if(error) return <h1>Something went wrong</h1>;
      return <App/>;
    }
    
    export default connect(
      state => ({
        error: state.error,
      })
    )(Wrapper);
    

    【讨论】:

    • 是的,这正是我最终所做的(参见下面与 Andrei 的讨论)。我想会有一种更内置的方式来做到这一点,但是......足够好!会接受这个作为答案,因为......伟大的思想。 :)
    【解决方案2】:

    来自React Error Boundaries docs

    错误边界不会捕获以下错误:

    • 事件处理程序(learn more)
    • 异步代码(例如 setTimeout 或 requestAnimationFrame 回调)
    • 服务器端渲染
    • 在错误边界本身(而不是其子项)中引发的错误

    【讨论】:

    • 这没什么用。这是子组件中发生的错误,它不是 SS 呈现的,它是在生命周期方法中,而不是事件处理程序中……而且我对异步限制并不完全清楚。我正在从 componentDidMount() 中调用一个 redux 操作,它是同步的……不是吗?这都是非常常见的,直截了当的东西。这是在说“你想做的事就是做不到”吗?
    • 正是因为异步限制。您的错误发生在异步获取请求期间,这不是旨在支持的场景错误边界
    • 好的,这更有意义。如何更好地处理这样的场景?我尝试用 try/catch 块包装 componentDidMount() 的内容,并且 catch 块永远不会触发......它不起作用。我只是在尝试通过 redux 为我的 action catch 块传递一个错误……但这感觉很笨拙。
    猜你喜欢
    • 2011-10-18
    • 1970-01-01
    • 2015-09-12
    • 2016-02-24
    • 1970-01-01
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    • 2013-09-01
    相关资源
    最近更新 更多