使用 React 错误边界和后备组件进行用户友好的错误
如果我们想防止我们的 UI 因错误而崩溃,并且还有一个后备 UI 以友好的方式显示这些错误,我们可以使用React 错误边界组件,它包裹我们应用程序的关键部分并在其子组件树中的任何位置捕获 JavaScript 错误。
此处有完整的带有打字稿的代码示例。
创建自定义错误边界组件
错误边界被创建为类组件,可以访问两种特殊的生命周期方法:
static getDerivedStateFromError()
更新其状态以显示后备 UI。componentDidCatch()
用于记录错误信息。
class ErrorBoundary extends React.Component {
state: State = {error: null}
static getDerivedStateFromError(error) {
return {error}
}
componentDidCatch(error, errorInfo) {
logErrorToMyService(error, errorInfo);
}
render() {
const {error} = this.state
if (error) {
return <this.props.FallbackComponent error={error} />
}
return this.props.children
}
}
在这个例子中,如果我们的 ErrorBoundary 捕获到错误,我们将传递一个FallbackComponent
要渲染的错误,并且我们将该错误记录到外部服务。
要在应用中使用 ErrorBoundary 组件,我们只需将其包装在一个可能出现错误的组件上。在本例中,我包装了一个从 API 获取数据的组件,并传递了一个 fallback 组件,用于在出现错误时显示错误消息:
<ErrorBoundary
// use key as a workaround for resetting the errorboundary state
key={circuitName}
FallbackComponent={CircuitErrorFallback}
>
<CircuitContent />
</ErrorBoundary>
function CircuitErrorFallback({error}) {
return (
<div role="alert">
<h3>Something went wrong...</h3>
<p>{error.message}</p>
</div>
)
}
如果我们的 API 调用出现问题,组件<CircuitContent />
将抛出错误:
function CircuitContent({circuitName}) {
const [state, setState] = useState<>({
status: 'idle',
circuit: {},
error: null,
})
const {status, circuit, error} = state
useEffect(() => {
if (!circuitName) {
return
}
setState(prevState => ({...prevState, status: 'pending'}))
fetchCircuit(circuitName).then(
circuit => {
setState(prevState => ({...prevState, status: 'resolved', circuit}))
},
error => {
setState(prevState => ({...prevState, status: 'rejected', error}))
},
)
}, [circuitName])
if (status === 'idle') {
return <CircuitIdle />
} else if (status === 'pending') {
return <CircuitLoading />
} else if (status === 'rejected') {
// throw error to be handled by error boundary
throw error
} else if (status === 'resolved') {
return <CircuitDetails circuit={circuit} />
}
throw new Error('Something went really wrong.')
}
ErrorBoundary 将捕获此错误并呈现我们的后备组件:
使用 react-error-boundary
创建我们自己的错误边界组件非常简单,但是我们也可以react-error-boundary
在我们的应用程序上安装包并使用它的功能来重置我们的错误边界并恢复我们的 UI 状态。
import {ErrorBoundary} from 'react-error-boundary'
<ErrorBoundary
onReset={handleReset}
resetKeys={[circuitName]}
FallbackComponent={CircuitErrorFallback}
>
<CircuitContent circuitName={circuitName} />
</ErrorBoundary>
现在我们可以使用按钮来扩展我们的回退组件,以重置错误边界:
function CircuitErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<h3>Something went wrong...</h3>
<p>{error.message}</p>
<button onClick={resetErrorBoundary}>
Try again
</button>
</div>
)
}
最终的错误 UI 将如下所示:
结论
我们可以用错误边界来包装应用程序的不同部分,以保持界面交互并防止崩溃。这在开发阶段也能帮助我们捕获TypeScript 可能忽略的错误。
使用 Create React App 时的注意事项:
即使错误边界捕获了错误, CRA 也可能会在开发模式下显示一个包含错误信息的覆盖层。有一些变通方法可以改变 Create React App 的这种行为,但我认为没有必要,因为你可以按 'esc' 来关闭覆盖层,而且这在生产版本中也不会显示。
使用 Axios 处理错误消息的提示:
当 API 调用失败时,Axios 会抛出一个自定义错误信息,例如“服务器响应了 404 状态码”。您可以使用 Axios 拦截器将此自定义信息更改为 API 响应主体中的实际错误消息,甚至可以将其映射到其他内容:
const api = axios.create({baseURL: 'https://api.backend.com'})
api.interceptors.response.use(
response => response,
error => {
if (error.response.data.message) {
error.message = error.response.data.message
}
return Promise.reject(error)
},
)
这篇文章的灵感来自于 epicreact.dev 上关于 React Hooks 研讨会的一节课。感谢阅读!
鏂囩珷鏉ユ簮锛�https://dev.to/leandrocoelho1/user-friend-errors-with-react-error-boundaries-and-fallback-components-mig