发布时间:2025-11-04 19:38:34 来源:创站工坊 作者:IT科技

人无完人,优雅的异常所以代码总会出错,捕获出错并不可怕,优雅的异常关键是捕获怎么处理。
我就想问问大家react的优雅的异常应用的错误怎么捕捉呢?这个时候:
小白+++:怎么处理? 小白++:ErrorBoundary 小白+:ErrorBoundary, try catch 小黑#: ErrorBoundary, try catch, window.onerror 小黑##: 这个是个严肃的问题,我知道N种处理方式,捕获你有什么更好的优雅的异常方案?EerrorBoundary是16版本出来的,有人问那我的捕获15版本呢,我不听我不听,优雅的异常反正我用16,捕获当然15有unstable_handleError。优雅的异常
关于ErrorBoundary官网介绍比较详细,捕获这个不是优雅的异常重点,重点是捕获他能捕捉哪些异常。
子组件的优雅的免费源码下载异常渲染 生命周期函数 构造函数 class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // Display fallback UI this.setState({ hasError: true }); // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } } <ErrorBoundary> <MyWidget /> </ErrorBoundary>开源世界就是好,早有大神封装了react-error-boundary[1] 这种优秀的库。
你只需要关心出现错误后需要关心什么,还以来个 Reset, 完美。
import {ErrorBoundary} from react-error-boundary function ErrorFallback({error, resetErrorBoundary}) { return ( <div role="alert"> <p>Something went wrong:</p> <pre>{error.message}</pre> <button onClick={resetErrorBoundary}>Try again</button> </div> ) } const ui = ( <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => { // reset the state of your app so the error doesnt happen again }} > <ComponentThatMayError /> </ErrorBoundary> )遗憾的是,error boundaries并不会捕捉这些错误:
事件处理程序 异步代码 (e.g. setTimeout or requestAnimationFrame callbacks) 服务端的渲染代码 error boundaries自己抛出的错误原文可见参见官网introducing-error-boundaries[2]
本文要捕获的就是 事件处理程序的错误。
官方其实也是有方案的how-about-event-handlers[3], 就是 try catch.
但是,那么多事件处理程序,我的天,得写多少,。。。。。。。。源码下载。。。。。。。。。。。。
handleClick() { try { // Do something that could throw } catch (error) { this.setState({ error }); } }我们先看看一张表格,罗列了我们能捕获异常的手段和范围。
异常类型 同步方法 异步方法 资源加载 Promise async/await
异常类型 同步方法 异步方法 资源加载 Promise async/await try/catch √ √ window.onerror √ √ error √ √ √ unhandledrejection √ √可以捕获同步和async/await的异常。
window.addEventListener(error) 这种可以比 window.onerror 多捕获资源记载异常. 请注意最后一个参数是 true, false的话可能就不如你期望。
当然你如果问题这第三个参数的含义,我就有点不想理你了。拜。
请注意最后一个参数是 true。
window.removeEventListener(unhandledrejection, this.onReject, true)其捕获未被捕获的Promise的异常。
XMLHttpRequest 很好处理,自己有onerror事件。当然你99.99%也不会自己基于XMLHttpRequest封装一个库, axios 真香,WordPress模板有这完毕的错误处理机制。
至于fetch, 自己带着catch跑,不处理就是你自己的问题了。
这么多,太难了。
还好,其实有一个库 react-error-catch[4] 是基于ErrorBoudary,error与unhandledrejection封装的一个组件。
其核心如下
ErrorBoundary.prototype.componentDidMount = function () { // event catch window.addEventListener(error, this.catchError, true); // async code window.addEventListener(unhandledrejection, this.catchRejectEvent, true); };使用:
import ErrorCatch from react-error-catch const App = () => { return ( <ErrorCatch app="react-catch" user="cxyuns" delay={5000} max={1} filters={[]} onCatch={(errors) => { console.log(报错咯); // 上报异常信息到后端,动态创建标签方式 new Image().src = `http://localhost:3000/log/report?info=${JSON.stringify(errors)}` }} > <Main /> </ErrorCatch>) } export default鼓掌,鼓掌。
其实不然:利用error捕获的错误,其最主要的是提供了错误堆栈信息,对于分析错误相当不友好,尤其打包之后。
错误那么多,我就先好好处理React里面的事件处理程序。
至于其他,待续。
我的思路原理很简单,使用decorator[5]来重写原来的方法。
先看一下使用:
@methodCatch({ message: "创建订单失败", toast: true, report:true, log:true }) async createOrder() { const data = {...}; const res = await createOrder(); if (!res || res.errCode !== 0) { return Toast.error("创建订单失败"); } ....... 其他可能产生异常的代码 ....... Toast.success("创建订单成功"); }注意四个参数:
message:出现错误时,打印的错误 toast:出现错误,是否Toast report: 出现错误,是否上报 log: 使用使用console.error打印可能你说,这这,消息定死,不合理啊。我要是有其他消息呢。
此时我微微一笑别急, 再看一段代码
@methodCatch({ message: "创建订单失败", toast: true, report:true, log:true }) async createOrder() { const data = {...}; const res = await createOrder(); if (!res || res.errCode !== 0) { return Toast.error("创建订单失败"); } ....... 其他可能产生异常的代码 ....... throw new CatchError("创建订单失败了,请联系管理员", { toast: true, report: true, log: false }) Toast.success("创建订单成功"); }是都,没错,你可以通过抛出 自定义的CatchError来覆盖之前的默认选项。
这个methodCatch可以捕获,同步和异步的错误,我们来一起看看全部的代码。
啥下一步,走一步看一步啦。
不,接下来的路,还很长。这才是一个基础版本。
1. 扩大成果,支持更多类型,以及hooks版本。
@XXXCatch classs AAA{ @YYYCatch method = ()=> { } }2. 抽象,再抽象,再抽象
玩笑开完了,严肃一下:
当前方案存在的问题:
功能局限 抽象不够 获取选项,代理函数, 错误处理函数完全可以分离,变成通用方法。 同步方法经过转换后会变为异步方法。 所以理论上,要区分同步和异步方案。 错误处理函数再异常怎么办之后,我们会围绕着这些问题,继续展开。
有掘友说,这个年代了,谁还不用Hooks。
是的,大佬们说得对,我们得与时俱进。
Hooks的基础版本已经有了,先分享使用,后续的文章跟上。
Hook的名字就叫useCatch
const TestView: React.FC<Props> = function (props) { const [count, setCount] = useState(0); const doSomething = useCatch(async function(){ console.log("doSomething: begin"); throw new CatchError("doSomething error") console.log("doSomething: end"); }, [], { toast: true }) const onClick = useCatch(async (ev) => { console.log(ev.target); setCount(count + 1); doSomething(); const d = delay(3000, () => { setCount(count => count + 1); console.log() }); console.log("delay begin:", Date.now()) await d.run(); console.log("delay end:", Date.now()) console.log("TestView", this) throw new CatchError("自定义的异常,你知道不") }, [count], { message: "I am so sorry", toast: true }); return <div> <div><button onClick={onClick}>点我</button></div> <div>{count}</div> </div> } export default React.memo(TestView);至于思路,基于useMemo,可以先看一下代码:
export function useCatch<T extends (...args: any[]) => any>(callback: T, deps: DependencyList, options: CatchOptions =DEFAULT_ERRPR_CATCH_OPTIONS): T { const opt = useMemo( ()=> getOptions(options), [options]); const fn = useMemo((..._args: any[]) => { const proxy = observerHandler(callback, undefined, function (error: Error) { commonErrorHandler(error, opt) }); return proxy; }, [callback, deps, opt]) as T; return fn; }error-boundaries[6]
React异常处理[7]
catching-react-errors[8]
react进阶之异常处理机制-error Boundaries[9]
decorator[10]
core-decorators[11]
autobind.js[12]