在我的Hello.jsx组件中,我调用的 API 可能会失败。这里调用了一个假 API loader:
import React, { useEffect } from "react";
export const Hello = () => {
const loader = async () => {
return Promise.reject("API error.");
};
useEffect(() => {
const load = async () => {
await loader();
};
load();
}, []);
return <h1>Hello World!</h1>;
};
Run Code Online (Sandbox Code Playgroud)
问题是该ErrorBoundary组件(此处未显示)应该打印一条红色消息,但事实并非如此。错误未被“捕获”。如果我throw通常不在异步函数内,它会显示红色文本“出了问题!”。有什么线索吗?
<div>
<ErrorBoundary>
<Hello />
</ErrorBoundary>
</div>
Run Code Online (Sandbox Code Playgroud)
Ste*_*ngs 17
传递给 useEffect 的函数已成功完成。它定义了一个函数,然后成功调用该函数。效果函数undefined正常返回,因此错误边界没有捕获任何内容。
错误边界并不能捕获所有可能的错误。具体来说,来自错误边界的文档:
错误边界不会捕获以下错误:
- 事件处理程序(了解更多)
- 异步代码(例如setTimeout或requestAnimationFrame回调)
- 服务端渲染
- 错误边界本身(而不是其子级)抛出的错误
如果您希望错误边界采取行动,那么一种选择是将错误保存在状态中并从组件主体中重新抛出它。例如:
function MyComponent() {
const [error, setError] = useState(null);
if (error) {
throw error;
}
useEffect(() => {
// If loading fails, save the error in state so the component throws when it rerenders
load().catch(err => setError(err));
}, []);
return <div>...</div>
}
Run Code Online (Sandbox Code Playgroud)
基本上,有两件事对你不利。第一个是 CRAErrorOverlay首先捕获错误,第二个是由于事件处理程序是异步的,它们不会触发componentDidCatch/getDerivedStateFromError生命周期:Issue #11409。
解决方法是unhandledrejection捕获window.
错误边界.js
import * as React from "react";
const ErrorBoundary = ({ children }) => {
const [error, setError] = React.useState("");
const promiseRejectionHandler = React.useCallback((event) => {
setError(event.reason);
}, []);
const resetError = React.useCallback(() => {
setError("");
}, []);
React.useEffect(() => {
window.addEventListener("unhandledrejection", promiseRejectionHandler);
return () => {
window.removeEventListener("unhandledrejection", promiseRejectionHandler);
};
/* eslint-disable react-hooks/exhaustive-deps */
}, []);
return error ? (
<React.Fragment>
<h1 style={{ color: "red" }}>{error.toString()}</h1>
<button type="button" onClick={resetError}>
Reset
</button>
</React.Fragment>
) : (
children
);
};
export default ErrorBoundary;
Run Code Online (Sandbox Code Playgroud)
你好.js
import React, { useEffect } from "react";
export const Hello = () => {
const loader = async () => {
return Promise.reject("API Error");
};
useEffect(() => {
const load = async () => {
try {
await loader();
} catch (err) {
throw err;
}
};
load();
}, []);
return <h1>Hello World!</h1>;
};
Run Code Online (Sandbox Code Playgroud)
索引.js
import React from "react";
import { render } from "react-dom";
import { Hello } from "./Hello";
import ErrorBoundary from "./ErrorBoundary";
const App = () => (
<ErrorBoundary>
<Hello />
</ErrorBoundary>
);
render(<App />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
更简洁的方法是仅显示有关错误的弹出窗口/通知,而不是覆盖整个 UI。更大、更复杂的 UI 意味着不必要的大量 UI 重绘:
| 归档时间: |
|
| 查看次数: |
11986 次 |
| 最近记录: |