在 React 项目中,处理错误以确保您的应用程序具有弹性、持续响应并提供最佳用户体验的最佳方法是什么?那离不开使用 Error Boundary。
Error Boundary 是什么?
错误边界是 React 处理应用程序错误的方法。它使得可以做出反应,可以显示错误,将其报告给错误报告服务,并从运行时错误中恢复,并在应用时提供备用用户界面。
错误边界是在 React 16 中引入的,它是需要作为类组件编写的唯一组件(所以目前还没有钩子!),但绝对应该成为任何现代 React 应用程序的一部分。一般,在整个应用程序的根级别创建单个错误边界即可。
错误边界是实现以下一种(或两种)方法的常规类组件:
- static getDerivedStateFromError(error):根据捕获的错误返回一个新状态。 通常将一个状态标志告诉错误边界是否提供回退用户界面。
-
componentDidCatch(error, errorInfo)
:每当发生错误时都会触发调用此方法。 可以将错误(和任何额外信息)记录到错误报告服务中,尝试从错误中恢复,以及需要做的任何其他事情。
具备以上两个基本功能的 Error Boundary 组件:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
errorService.log({ error, errorInfo });
}
render() {
if (this.state.hasError) {
return (
<div>
<h1>糟糕,被我们搞砸了</h1>
<button type="button" onClick={() => this.setState({ hasError: false })}>
再试一次?
</button>
</div>
);
}
return this.props.children;
}
}
如何使用:
ReactDOM.render(
<ErrorBoundary>
<App />
</ErrorBoundary>,
document.getElementById('root')
)
更好的 Error Boundary
错误边界非常适合捕获渲染期间未预料到的运行时错误。 但是,有几种类型的错误没有被捕获,需要以不同的方式处理这些错误。 这些包括:
- 事件处理程序中的错误(例如,单击按钮时)
- 异步回调中的错误(例如 setTimeout)
- 错误边界组件本身发生的错误
- 服务器端渲染期间发生的错误
这些限制听起来可能很严重,但大多数时候可以通过使用 try-catch 和类似的 hasError 状态来解决它们。
function SignUpButton(props) {
const [hasError, setError] = React.useState(false);
const handleClick = async () => {
try {
await api.signUp();
} catch(error) {
errorService.log({ error })
setError(true);
}
}
if (hasError) {
return <p>登录失败</p>;
}
return <button onClick={handleClick}>登录</button>;
}
但是,我们可以通过上下文 API 实现在事件处理程序和异步位置重用错误边界的错误处理逻辑:
export const ErrorBoundaryContext = React.createContext(() => {});
export const useErrorHandling = () => {
return React.useContext(ErrorBoundaryContext)
}
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
errorService.log({ error, errorInfo });
}
triggerError = ({ error, errorInfo }) => {
errorService.log({ error, errorInfo });
this.setState({ hasError: true });
}
resetError = () => this.setState({ hasError: false });
render() {
return (
<ErrorBoundaryContext.Provider value={this.triggerError}>
{this.state.hasError
? <div>
<h1>糟糕,被我们搞砸了</h1>
<button type="button" onClick={() => this.setState({ hasError: false })}>再试一次?</button>
</div>
: this.props.children
}
</ErrorBoundaryContext.Provider>
);
}
}
function SignUpButton(props) {
const { triggerError } = useErrorHandling();
const handleClick = async () => {
try {
await api.signUp();
} catch(error) {
triggerError(error);
}
}
return <button onClick={handleClick}>登录</button>;
}
使用 react-error-boundary
React Core 团队成员 Brian Vaughn 创建的错误边界组件 [react-error-boundary],它提供与上面几乎相同的能力,支持传入自定义备用组件和重置逻辑。
ReactDOM.render(
<ErrorBoundary
FallbackComponent={MyFallbackComponent}
onError={(error, errorInfo) => errorService.log({ error, errorInfo })}
>
<App />
</ErrorBoundary>,
document.getElementById('root')
)
自己编写 error boundary,或采用 react-error-boundary 库这两种方式让应用程序优雅地失败,甚至可以在应用程序的某些部分包含错误而其余部分继续工作,都是可取的。
开源的会话重播
OpenReplay 是一个开源的会话重播套件,可查看用户在 Web 应用程序上所做的事情,从而帮助更快地解决问题。 OpenReplay 是自托管的,可以完全控制数据。