React:在应用程序中处理错误(Error Boundary)
引言
在这篇文章中,我们将探讨如何在React中自带的处理错误。React提供了一种错误边界机制,称为Error Boundary,可以用来捕获应用程序中发生的错误。接下来,我们将详细介绍如何使用Error Boundary进行错误处理。
环境
- 操作系统:Windows 10
- Vite版本:v2.7.2
- Node版本:v19.0.0
- React版本:v19.0.0
- TypeScript版本:v5
创建一个没有错误的示例程序
首先,我们将创建一个没有错误的示例程序。
以下是具体代码:
main.tsx
import App from "./App";
export default function Home() {
return <App />;
}
components/App.tsx
import Page1 from "@/components/Page1";
import Page2 from "@/components/Page2";
import Page3 from "@/components/Page3";
export default function App() {
return (
<>
<Page1 />
<Page2 />
<Page3 />
</>
);
}
components/Page1.tsx
export default function Page1() {
return (
<div style={{ backgroundColor: "lightblue", paddingBottom: "20px" }}>
<h3>Page1</h3>
</div>
);
}
components/Page2.tsx
export default function Page2() {
return (
<div style={{ backgroundColor: "yellow", paddingBottom: "20px" }}>
<h3>Page2</h3>
</div>
);
}
components/Page3.tsx
import Page3Child from "./Page3Child";
export default function Page3() {
return (
<div style={{ backgroundColor: "grey", paddingBottom: "20px" }}>
<h3>Page3</h3>
<Page3Child />
</div>
);
}
components/Page3Child.tsx
export default function Page3Child() {
return (
<div style={{ backgroundColor: "orange", paddingBottom: "20px" }}>
<h5>Page 3 Child</h5>
</div>
);
}
在组件中发生错误
接下来,为了演示Error Boundary是如果捕获并处理异常的。我们将修改Page3Child组件的代码,使其发生错误。
components/Page3Child.tsx
function Page3Child() {
const title = 'Page3Child';
return (
<div style={{ backgroundColor: '#DEB331' }}>
{/* hoge未声明,因此会引发错误 */}
<h5>{hoge}</h5>
</div>
);
}
export default Page3Child;
当组件中发生错误时,整个页面会跳转错误信息页面,控制台中会输出错误信息。
安装ErrorBoundary
为了处理错误,我们使用react自带的ErrorBoundary。不需要安装包命令进行安装。
创建ErrorBoundary组件
接下来,我们将创建一个ErrorBoundary组件,用于处理子组件树中发生的错误。ErrorBoundary组件将使用类组件来创建,并定义static getDerivedStateFromError()和componentDidCatch()方法。static getDerivedStateFromError()用于在错误被抛出后渲染备用UI,而componentDidCatch()用于记录错误信息(可选)。
根据官方文档,定义了生命周期方法static getDerivedStateFromError()或componentDidCatch()的类组件将成为Error Boundary。如果省略static getDerivedStateFromError()方法,则不会显示备用UI(错误信息组件),ErrorBoundary组件本身将会出错。
错误信息如下:
ErrorBoundary: Error boundaries should implement getDerivedStateFromError(). In that method, return a state update to display an error message or fallback UI. 警告中文:ErrorBoundary:错误边界应该实现getDerivedStateFromError()。在该方法中,返回一个状态更新以显示错误消息或备用UI。
components/ErrorBoundary.tsx
import React, { Component, ErrorInfo } from "react";
type Props = {
children: React.ReactNode;
};
type State = {
hasError: boolean;
};
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error) {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("Uncaught error:", error, errorInfo);
}
render(): React.ReactNode {
return this.state.hasError ? <h1>发生错误</h1> : this.props.children;
}
}
export default ErrorBoundary;
我们将使用创建的ErrorBoundary组件来包裹Page3组件中的Page3Child组件。
components/Page3.tsx
"use client";
import ErrorBoundary from "./ErrorBoundary";
import Page3Child from "./Page3Child";
export default function Page3() {
return (
<div style={{ backgroundColor: "grey", paddingBottom: "20px" }}>
<h3>Page3</h3>
<ErrorBoundary>
<Page3Child />
</ErrorBoundary>
</div>
);
}
Page3Child组件将引发错误。
components/Page3Child.tsx
export default function Page3Child() {
const title = "Page3Child";
return (
<div style={{ backgroundColor: "orange", paddingBottom: "20px" }}>
{/* hoge未声明,因此会引发错误 */}
<h5>{hoge}</h5>
</div>
);
}
当用ErrorBoundary组件包裹后,Page3Child组件部分将显示ErrorBoundary定义的异常信息组件。
修改ErrorBoundary组件的包裹范围
我们将尝试修改ErrorBoundary组件的包裹范围。首先,我们将删除在Page3组件中使用的ErrorBoundary组件,并在App组件中包裹Page3组件。
components/App.tsx
"use client";
import Page1 from "@/components/Page1";
import Page2 from "@/components/Page2";
import Page3 from "@/components/Page3";
import ErrorBoundary from "@/components/ErrorBoundary";
export default function App() {
return (
<>
<Page1 />
<Page2 />
<ErrorBoundary>
<Page3 />
</ErrorBoundary>
</>
);
}
此时,Page3部分将显示ErrorBoundary定义的异常信息组件。
接下来,我们将Page2和Page3组件包裹在ErrorBoundary组件中。
components/App.tsx
"use client";
import Page1 from "@/components/Page1";
import Page2 from "@/components/Page2";
import Page3 from "@/components/Page3";
import ErrorBoundary from "@/components/ErrorBoundary";
export default function App() {
return (
<>
<Page1 />
<ErrorBoundary>
<Page2 />
<Page3 />
</ErrorBoundary>
</>
);
}
Page2和Page3的内容没有显示,而替换成了ErrorBoundary定义的异常信息组件
最后,我们将所有组件都包裹在ErrorBoundary组件中。
components/App.tsx
export default function App() {
return (
<>
<ErrorBoundary>
<Page1 />
<Page2 />
<Page3 />
</ErrorBoundary>
</>
);
}
此时,整个页面内容没有显示,而完全替换成了ErrorBoundary定义的异常信息组件。
Error Boundary无法捕获的错误
接下来我们介绍一下Error Boundary的局限性,React自带的Error Boundary无法捕获以下类型的错误:
- 在事件处理程序中发生的错误
- 在异步处理中的错误
- 服务器端渲染中发生的错误
- Error Boundary自身抛出的错误
在事件处理程序中引发错误
我们将尝试在事件处理程序中引发错误。我们将创建一个新的Page4组件。
components/Page4.tsx
export function Page4() {
const onClick = () => {
throw new Error("Page4 error");
};
return (
<div style={{ padding: "20px", backgroundColor: "lightgray" }}>
<h3>Page4</h3>
<button type="button" onClick={onClick}>
按钮
</button>
</div>
);
}
当点击Page4的按钮时,控制台中会显示错误,但页面不会发生任何变化。
在异步处理过程中引发错误
接下来,我们将尝试在异步处理过程中引发错误。我们将修改Page4组件以避免引发错误,并创建一个新的Page5组件。
components/Page5.tsx
import { useEffect, useState } from "react";
type UserType = {
name: string;
age: number;
};
const fetchUserApi = async (): Promise<UserType> => {
const user = { name: "John Doe", age: 30 };
throw new Error("fetchUserAPI报错");
return user;
};
function Page5() {
const [user, setUser] = useState<UserType>();
useEffect(() => {
const fetchData = async () => {
setUser(await fetchUserApi());
};
fetchData();
}, []);
return (
<div style={{ backgroundColor: "pink", padding: "20px" }}>
<div>
<h3>Page5</h3>
<p>用户名:{user?.name}</p>
</div>
</div>
);
}
export default Page5;
同样,控制台中会显示错误,但页面不会发生任何变化。
总结
使用Error Boundary进行错误处理非常简单。然而,对于在事件处理程序中引发的错误和异步处理中的错误,Error Boundary并没有提供有效的解决方案。下一步,我将研究如何使用“react-error-boundary”来处理组件中引发的错误、事件处理程序中的错误以及异步处理中的错误。
希望这篇文章能帮助你更好地理解React中的错误处理机制!