从 v16.2.0 开始,无法将功能组件转变为错误边界。
React 文档对此很清楚,尽管您可以随意重复使用它们:
方法的工作方式类似于 JavaScriptcomponentDidCatch()
块,但适用于组件。 只有类组件可以作为错误边界。在实践中,大多数时候您需要声明一次错误边界组件并在整个应用程序中使用它。catch {}
另请记住,
try/catch
块不适用于所有情况。try/catch
块将不起作用 - 因为它不一定与子级一起更新。
正如已经提到的,React 团队尚未实现等效的钩子,并且钩子实现没有发布时间表。
npm 上的一些第三方包实现了错误边界挂钩。我发布了 react-use-error-boundary,尝试重新创建一个类似于 Preact useErrorBoundary 的 API:
import { withErrorBoundary, useErrorBoundary } from "react-use-error-boundary";
const App = withErrorBoundary(({ children }) => {
const [error, resetError] = useErrorBoundary(
// You can optionally log the error to an error reporting service
(error, errorInfo) => logErrorToMyService(error, errorInfo)
);
if (error) {
return (
<div>
<p>{error.message}</p>
<button onClick={resetError}>Try again</button>
</div>
);
}
return <div>{children}</div>;
});
componentDidCatch
和
deriveStateFromError
。据作者介绍,它是基于React.memo()的。
所提出的解决方案深受新的 React.memo() API 的启发。
import Catch from "./functional-error-boundary"
type Props = {
children: React.ReactNode
}
const MyErrorBoundary = Catch(function MyErrorBoundary(props: Props, error?: Error) {
if (error) {
return (
<div className="error-screen">
<h2>An error has occured</h2>
<h4>{error.message}</h4>
</div>
)
} else {
return <React.Fragment>{props.children}</React.Fragment>
}
})
参考和 API
class component
并将我的
functional/class
组件包装在其中需要的地方。这就是我的自定义类组件的样子:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {error: ""};
}
componentDidCatch(error) {
this.setState({error: `${error.name}: ${error.message}`});
}
render() {
const {error} = this.state;
if (error) {
return (
<div>{error}</div>
);
} else {
return <>{this.props.children}</>;
}
}
}
并像这样使用我的functional/class
组件:
<ErrorBoundary key={uniqueKey}>
<FuncationalOrChildComponent {...props} />
</ErrorBoundary>
PS:像往常一样, key 属性非常重要,因为如果您有动态子元素,它将确保重新渲染 ErrorBoundary
组件。
Sentry.ErrorBoundary
import React from "react";
import * as Sentry from "@sentry/react";
<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
<Example />
</Sentry.ErrorBoundary>;
或作为高阶组件
import React from "react";
import * as Sentry from "@sentry/react";
Sentry.withErrorBoundary(Example, { fallback: <p>an error has occurred</p> });
import React, { useState, useEffect } from 'react';
import { Button } from 'antd';
const ErrorBoundary = ({ children }) => {
const [hasError, setHasError] = useState(false);
useEffect(() => {
const errorHandler = (event) => {
setHasError(true);
console.error("ErrorBoundary caught an error", event.error);
};
window.addEventListener("error", errorHandler);
window.addEventListener("unhandledrejection", errorHandler);
return () => {
window.removeEventListener("error", errorHandler);
window.removeEventListener("unhandledrejection", errorHandler);
};
}, []);
if (hasError) {
return (
<div style={styles.container}>
<div style={styles.errorBox}>
<h1 style={styles.title}>Oops! Something went wrong.</h1>
<p style={styles.message}>
We're sorry for the inconvenience. Please try again later or contact support if the issue persists.
</p>
<Button type="primary" style={styles.button}>
<a href="/profile">GO BACK</a>
</Button>
</div>
</div>
);
}
return children;
};
const styles = {
container: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
backgroundColor: '#f0f2f5',
},
errorBox: {
textAlign: 'center',
backgroundColor: '#fff',
padding: '40px',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)',
},
title: {
fontSize: '24px',
color: '#ff4d4f',
},
message: {
fontSize: '16px',
color: '#595959',
marginBottom: '20px',
},
button: {
marginTop: '20px',
},
};
export default ErrorBoundary;
您可以使用这个基于功能的错误边界组件