React ErrorBoundary 没有捕捉到 "cannot read properties of undefined"

React ErrorBoundary doesn't catch "cannot read properties of undefined"

我在使用 React ErrorBoundary 时遇到了一个奇怪的边缘案例。如果异常发生在内联表达式中,则不会处理异常。例如:

const page = webtexts[courseId].pages[pageFamilyId]; // Returns undefined
...
<ErrorBoundary>
    // "Cannot read properties of undefined (reading 'page_name')" won't be processed in ErrorBoundary
    <span>{page.page_name}</span>
</ErrorBoundary>

但是如果我在组件中包裹了一个有问题的地方,它就可以正常工作。

const page = webtexts[courseId].pages[pageFamilyId]; // Returns undefined
...
const WrapFailingPage: React.FC<{ page: any }> = ({ page }) => {
    return <span>{page.page_name}</span>;
};
...
<ErrorBoundary>
    <WrapFailingPage page={page} /> // The error is properly processed in ErrorBoundary
</ErrorBoundary>

为了演示,我制作了一个简单的 Codepen - https://codepen.io/OlexanderD/pen/wvpdpxY

ErrorBoundary 应该那样做吗?为什么内联表达式会出现问题?

TL;DR

ErrorBoundary 仅跟踪使用 React.createElement(React.Component / React.[V]FC) 创建的代码部分。因此,如果您 return 来自自定义 React 组件的损坏的 JSX,ErrorBoundary 将拾取它并进行处理。

详情:

示例是为随附的代码笔制作的https://codepen.io/OlexanderD/pen/wvpdpxY

一些术语:

  • element - 任何 HTML 标签。它将被处理为 React.createElement('div', ...)
  • component - 任何 React 组件,而不是原生 HTML 标签。我将被处理为 React.createElement(MyComponent, ...)

根据 the docsErrorBoundary 只能在其子 component 树 中处理异常 ,但我试图从 元素 .

捕获异常

React.createElement() can receives 2 types of arguments: tag name (div, span, ...) or React component. Therefore, when we simply use <div>{fail.absurdIntentionalError}</div> and pass it to parent div the React will generate this code:

React.createElement(
  "div",
  null,
  React.createElement("button", ...),
  showFailingChild &&
  React.createElement("div", null, fail.absurdIntentionalError)
);  

如您所见,我们传递了一个 元素 作为父 props.children 的一部分20=]。因此 ErrorBoundary 不会处理 它作为 component tree 的一部分。

但是如果我们使用像 FailingComponent 这样的包装器,生成的 JSX 将看起来 like this:

React.createElement(
  "div",
  null,
  React.createElement("button", ...),
  showFailingComponent &&
  React.createElement(FailingComponent, null)
);  

现在我们将 组件 传递给父 divprops.childrenErrorBoundary 将处理 FailingComponent 的结果作为 component tree.[=36= 的一部分]