原文地址:React error handling with react-error-boundary | Saeloun Blog
当我们在编写React程序的时候,不可避免的会遇到报错。但即使在编码的时候很严谨,在程序运行时也还是会有不符合预期的报错出现。而这些错误很可能会导致我们的网站完全崩溃,对用户体验造成影响。这就是为什么我们捕获和处理错误的方式尤为重要。
React Error Boundary可以让开发者捕获并处理错误,同时还能防止整个程序崩溃,确保为用户提供顺滑的体验。
React Error Boundaries
React Error Boundary在提供了一种优雅的方式处理错误的同时,又不会让这个应用崩溃掉。这个API允许开发者在某些特定组件内部捕获和处理错误。这些组件提供了降级兜底的UI渲染,而不是直接让应用白屏。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
logErrorToMyService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
// we can render any custom fallback UI
return this.props.fallback;
}
return this.props.children;
}
}
我们可以在组件外层包一个ErrorBoundary
<ErrorBoundary fallback={<p>Error occurred.</p>}>
<MyComponent />
</ErrorBoundary>
React Error Boundary 在提供了丝滑无缝的用户体验的方式同时,也有一些限制存在。
React Error Boundary的限制
事件回调和异步代码的错误
React Error Boundary 只能捕获出现在渲染阶段的错误。他们不能捕获出现在事件回调,异步代码(setTimeout,fetch)或者是SSR中的错误
class MyComponent extends React.Component {
handleClick() {
// This error will not be caught by the error boundary
throw new Error('Error in event handler');
}
render() {
return <button onClick={handleClick}>Click me</button>;
}
}
出现在render boundary中的错误
React Error Boundary不能捕获出现在自身组件中render方法中出现的错误
这就到了react-error-boundary这个库发挥作用的时候了。这个库提供了更灵活的错误捕获和处理的方式,使得开发者能够打造更稳定和更用户友好的错误处理机制。
这个库提供了大量的属性能够对于 Error Boundary 的行为来定制化。让我们来看看几个重要的属性和用法:
- FallbackComponent:当 Error Boundary 内部捕获到错误的时候,该属性能够让我们指定一个自定义组件渲染。这个API能够让我们更灵活地编写一个视觉效果友好且有信息的UI。同时,除了展示错误信息之外还可以提供一些其他的行为。
- fallbackRender,与
fallbackcomponent类似,该属性让开发者自定义一个渲染函数来渲染兜底错误UI。不同的是,对于渲染过程自定义程度更高,还能够做一些更高级的错误兜底逻辑处理。 - onError,该属性接受一个回调函数,当错误被 Error Boundary 捕获的时候会调用这个函数,同时将组件的错误堆栈传递过来。我们可以使用这个属性做一些错误日志上报等等。
- onReset,这个属性接受一个回调函数。当一个错误出现后,Error Boundary 成功地重置时,会调用这个函数。当错误恢复之后,清理一些行为和更新组件的状态是很好用的。
- fallbackProps,这个属性可以让我们透传一些额外的属性给
FallbackComponent或者fallbackRender函数。给错误兜底UI传递一些额外的数据时是很好用的。 - retry,这个属性是一个布尔值。布尔值来决定 Error Boundary 是否来重试导致错误异常的操作。当该属性为true的时候,
resetErrorBoundary属性传入的回调函数就会被调用来触发重试操作。
import React, { useState } from "react";
import { ErrorBoundary } from "react-error-boundary";
const App = () => {
const ErrorFallback = ({ error }) => {
// we can customize the UI as we want
return (
<div
style="color:red"
>
<h2>
Oops! An error occurred
<br />
<br />
{error.message}
</h2>
{/* Additional custom error handling */}
</div>
);
};
const logError = (error) => {
setErrorMessage(error.message);
console.error(error);
// we can also send the error to a logging service
};
const handleResetError = () => {
console.log("Error boundary reset");
setErrorMessage("");
//additional logic to perform code cleanup and state update actions
};
const UserProfile = ({ user }) => {
return (
<div>
<h2>User Profile</h2>
<p>Name: {user.name}</p>
<p>Email: {user.personalID.email}</p>
</div>
);
};
const user = {
name: 'Shruti Apte',
//missing personalID.email property on purpose
};
const [errorMessage, setErrorMessage] = useState("");
return (
<ErrorBoundary
onError={logError}
onReset={handleResetError}
FallbackComponent={ErrorFallback}
>
<UserProfile user={user} />
</ErrorBoundary>
);
};
export default App;
以下是react-error-boundary捕获到错误后的UI行为
我们还可以使用
fallbackRender属性而不是FallbackComponent。只需要传一个内敛函数作为属性就可以了。
<ErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) => (
<div>
<h2>An error occurred: {error.message}</h2>
<button onClick={resetErrorBoundary}>Retry</button>
</div>
)}
>
{/* Component code */}
</ErrorBoundary>
至于说这两个属性使用哪一个完全依靠我们的喜好和错误降级兜底UI的复杂度。如果我们更倾向于单独抽离出一个组件,且这个组件有自己的渲染逻辑,FallbackComponent是一个合适的选择。另一方面如果我们需要对渲染错误兜底UI有更大的灵活度,那么fallbackRender能够让我们定义一个内敛的render函数。
useErrorBoundary hook
这个库还提供了一个useErrorBoundary的hook。这个hook能够让开发者使用更简洁,函数式的方式来打造 Error Boundary 的UI。
我们先引入这个hook
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
现在我们修改上面的例子ErrorFallback来看看这个hooks到底是怎么用的
const ErrorFallback = ({ error}) => {
const { resetErrorBoundary } = useErrorBoundary();
// we can customize the UI as we want
return (
<div>
<h2 style="color: red">Oops! An error occurred: {error.message}</h2>
<button onClick={resetErrorBoundary}>Retry</button>
</div>
);
};
resetErrorBoundary 函数能够让用户重置 Error Boundary 并且尝试重渲染 UserProfile 组件。我们也可以传一个 onReset 属性来重置这个组件。
withErrorBoundary HOC
这个库还提供了高阶组件,接收的属性跟上面的例子保持一致。
import { withErrorBoundary } from 'react-error-boundary';
我们把 UserProfile 组件包在 withErrorBoundary 的高阶组件内部。
const App = () => {
const user = {
name: 'Shruti Apte',
// Missing email property intentionally to trigger an error
};
const UserProfileWithBoundary = withErrorBoundary(UserProfile, {
FallbackComponent: ErrorFallback,
});
return (
<UserProfileWithBoundary user={user} />
//we can also render other components wrapped inside withErrorBoundary HOC.
);
};
export default App;
通过使用 withErrorBoundary 的hoc,我们可以很轻松地将error boundry应用到多个组件上。同时不用重复写错误处理的模版代码。
总结
React Error Boundary 是在组件内部错误处理的很有用的工具,但是同时它自身也有很多限制,比如不能捕获在事件回调中,异步代码中和自身报错里的错误。react-error-boundary的库提供了额外的特性和灵活度解决了这些限制,比如自定义报错兜底组件和重试机制。通过使用这个库。开发者能够在程序运行时更好的处理错误并提供更好的用户体验。