React 错误边界组件 react-error-boundary 源码解析

本文主要是介绍React 错误边界组件 react-error-boundary 源码解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 捕获错误 hook
  • 创建错误边界组件 Provider
  • 定义错误边界组件
    • 定义边界组件状态
    • 捕捉错误
    • 渲染备份组件
    • 重置组件
    • 通过 useHook 控制边界组件

捕获错误 hook

  • getDerivedStateFromError
    • 返回值会作为组件的 state 用于展示错误时的内容
  • componentDidCatch

创建错误边界组件 Provider

  • 错误边界组件其实是一个通过 Context.Provider 包裹的组件,这样使得组件内部可以获取到捕捉的相关操作
import { createContext } from "react";export type ErrorBoundaryContextType = {didCatch: boolean;error: any;resetErrorBoundary: (...args: any[]) => void;
};// 错误边界组件其实是一个通过 Context.Provider 包裹的组件
export const ErrorBoundaryContext =createContext<ErrorBoundaryContextType | null>(null);

定义错误边界组件

定义边界组件状态

type ErrorBoundaryState =| {didCatch: true;error: any;}| {didCatch: false;error: null;};const initialState: ErrorBoundaryState = {didCatch: false, // 错误是否捕捉error: null, // 捕捉到的错误信息
};

捕捉错误

  • getDerivedStateFromError 捕捉到错误后,设置组件状态展示备份组件
  • componentDidCatch 用于触发错误回调
export class ErrorBoundary extends Component<ErrorBoundaryProps,ErrorBoundaryState
> {constructor(props: ErrorBoundaryProps) {super(props);this.resetErrorBoundary = this.resetErrorBoundary.bind(this);this.state = initialState;}static getDerivedStateFromError(error: Error) {return { didCatch: true, error };}componentDidCatch(error: Error, info: ErrorInfo) {this.props.onError?.(error, info);}}

渲染备份组件

  • 通过指定的参数名区分是无状态组件还是有状态组件
    • 无状态组件通过直接调用函数传递 props
    • 有状态组件通过 createElement 传递 props
  • 通过 createElement 处理传递的组件更加优雅
    • createElement(元素类型,参数,子元素)详情,其中第一个参数可以直接传递 Context.Provider
export class ErrorBoundary extends Component<ErrorBoundaryProps,ErrorBoundaryState
> {// ...render() {const { children, fallbackRender, FallbackComponent, fallback } =this.props;const { didCatch, error } = this.state;let childToRender = children;// 如果捕捉到了错误if (didCatch) {const props: FallbackProps = {error,resetErrorBoundary: this.resetErrorBoundary,};// 通过指定的参数名区分是无状态组件还是有状态组件if (typeof fallbackRender === "function") {childToRender = fallbackRender(props);} else if (FallbackComponent) {childToRender = createElement(FallbackComponent, props);} else if (fallback === null || isValidElement(fallback)) {childToRender = fallback;} else {if (isDevelopment) {console.error("react-error-boundary requires either a fallback, fallbackRender, or FallbackComponent prop");}throw error;}}// Context.Provider 可以直接作为 createElement 的第一个参数return createElement(ErrorBoundaryContext.Provider,{value: { // Context.Provider 提供可供消费的内容didCatch,error,resetErrorBoundary: this.resetErrorBoundary,},},childToRender);}// ...
}

重置组件

  • 将错误信息重置使得能渲染原组件
const initialState: ErrorBoundaryState = {didCatch: false, // 错误是否捕捉error: null, // 捕捉到的错误信息
};export class ErrorBoundary extends Component<ErrorBoundaryProps,ErrorBoundaryState
> {// ...resetErrorBoundary(...args: any[]) {const { error } = this.state;if (error !== null) {this.props.onReset?.({ // 触发对应回调args,reason: "imperative-api",});this.setState(initialState);}}// ...// 根据 resetKeys 重置,但并未对外暴露该 APIcomponentDidUpdate(prevProps: ErrorBoundaryProps,prevState: ErrorBoundaryState) {const { didCatch } = this.state;const { resetKeys } = this.props;// There's an edge case where if the thing that triggered the error happens to *also* be in the resetKeys array,// we'd end up resetting the error boundary immediately.// This would likely trigger a second error to be thrown.// So we make sure that we don't check the resetKeys on the first call of cDU after the error is set.if (didCatch &&prevState.error !== null &&hasArrayChanged(prevProps.resetKeys, resetKeys)) {this.props.onReset?.({next: resetKeys,prev: prevProps.resetKeys,reason: "keys",});this.setState(initialState);}}
}function hasArrayChanged(a: any[] = [], b: any[] = []) {return (a.length !== b.length || a.some((item, index) => !Object.is(item, b[index])));
}

通过 useHook 控制边界组件

  • 通过 context 获取最近的边界组件内容
  • 通过手动抛出错误重新触发边界组件
import { useContext, useMemo, useState } from "react";
import { assertErrorBoundaryContext } from "./assertErrorBoundaryContext";
import { ErrorBoundaryContext } from "./ErrorBoundaryContext";type UseErrorBoundaryState<TError> =| { error: TError; hasError: true }| { error: null; hasError: false };export type UseErrorBoundaryApi<TError> = {resetBoundary: () => void;showBoundary: (error: TError) => void;
};export function useErrorBoundary<TError = any>(): UseErrorBoundaryApi<TError> {// 获取最近的边界组件 Provider 的内容const context = useContext(ErrorBoundaryContext);// 断言 Context 是否为空assertErrorBoundaryContext(context);const [state, setState] = useState<UseErrorBoundaryState<TError>>({error: null,hasError: false,});const memoized = useMemo(() => ({resetBoundary: () => {// 提供 Provider 对应的重置边界组件方法,渲染原组件context.resetErrorBoundary();setState({ error: null, hasError: false });},// 手动抛出错误,触发边界组件showBoundary: (error: TError) =>setState({error,hasError: true,}),}),[context.resetErrorBoundary]);// 当调用 showBoundary 后,该 hook 会手动抛出错误,让边界组件来捕捉if (state.hasError) {throw state.error;}return memoized;
}

这篇关于React 错误边界组件 react-error-boundary 源码解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/684127

相关文章

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

在MySQL执行UPDATE语句时遇到的错误1175的解决方案

《在MySQL执行UPDATE语句时遇到的错误1175的解决方案》MySQL安全更新模式(SafeUpdateMode)限制了UPDATE和DELETE操作,要求使用WHERE子句时必须基于主键或索引... mysql 中遇到的 Error Code: 1175 是由于启用了 安全更新模式(Safe Upd

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

css渐变色背景|<gradient示例详解

《css渐变色背景|<gradient示例详解》CSS渐变是一种从一种颜色平滑过渡到另一种颜色的效果,可以作为元素的背景,它包括线性渐变、径向渐变和锥形渐变,本文介绍css渐变色背景|<gradien... 使用渐变色作为背景可以直接将渐China编程变色用作元素的背景,可以看做是一种特殊的背景图片。(是作为背

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

SpringBoot中的404错误:原因、影响及解决策略

《SpringBoot中的404错误:原因、影响及解决策略》本文详细介绍了SpringBoot中404错误的出现原因、影响以及处理策略,404错误常见于URL路径错误、控制器配置问题、静态资源配置错误... 目录Spring Boot中的404错误:原因、影响及处理策略404错误的出现原因1. URL路径错

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

在Rust中要用Struct和Enum组织数据的原因解析

《在Rust中要用Struct和Enum组织数据的原因解析》在Rust中,Struct和Enum是组织数据的核心工具,Struct用于将相关字段封装为单一实体,便于管理和扩展,Enum用于明确定义所有... 目录为什么在Rust中要用Struct和Enum组织数据?一、使用struct组织数据:将相关字段绑