diff --git a/src/renderer/src/components/common/AppErrorBoundary.tsx b/src/renderer/src/components/common/AppErrorBoundary.tsx new file mode 100644 index 0000000..d5620db --- /dev/null +++ b/src/renderer/src/components/common/AppErrorBoundary.tsx @@ -0,0 +1,63 @@ +import { Component, type ErrorInfo, type ReactNode } from 'react' +import i18n from '../../i18n' + +interface AppErrorBoundaryProps { + children: ReactNode +} + +interface AppErrorBoundaryState { + error: Error | null +} + +/** + * 顶层错误边界。 + * + * 没有它时,任何渲染期异常都会让 React 卸载整棵组件树,窗口直接变白,用户和日志都 + * 拿不到线索。这里把异常收敛成一个可恢复的错误页,并把原始错误写进控制台。 + * + * 用 i18n 实例而不是 useTranslation:错误页必须能在 React 上下文之外渲染。 + */ +export default class AppErrorBoundary extends Component< + AppErrorBoundaryProps, + AppErrorBoundaryState +> { + state: AppErrorBoundaryState = { error: null } + + static getDerivedStateFromError(error: Error): AppErrorBoundaryState { + return { error } + } + + componentDidCatch(error: Error, info: ErrorInfo): void { + console.error('[AppErrorBoundary] Uncaught render error:', error, info.componentStack) + } + + private handleReload = (): void => { + window.location.reload() + } + + render(): ReactNode { + const { error } = this.state + if (!error) { + return this.props.children + } + + return ( +
+

{i18n.t('common:errorTitle')}

+

+ {i18n.t('common:errorDescription')} +

+
+          {error.message}
+        
+ +
+ ) + } +} diff --git a/src/renderer/src/locales/en-US/common.json b/src/renderer/src/locales/en-US/common.json index 4cb23e4..d812378 100644 --- a/src/renderer/src/locales/en-US/common.json +++ b/src/renderer/src/locales/en-US/common.json @@ -32,6 +32,9 @@ "about": "About", "version": "Version", "error": "Error", + "errorTitle": "Something went wrong", + "errorDescription": "An unexpected error occurred. Reload to continue.", + "reload": "Reload", "warning": "Warning", "info": "Info", "success": "Success" diff --git a/src/renderer/src/locales/zh-CN/common.json b/src/renderer/src/locales/zh-CN/common.json index 9b2633c..6b425e9 100644 --- a/src/renderer/src/locales/zh-CN/common.json +++ b/src/renderer/src/locales/zh-CN/common.json @@ -8,5 +8,8 @@ "edit": "编辑", "loading": "加载中...", "error": "错误", + "errorTitle": "出错了", + "errorDescription": "发生了意外错误,请重新加载以继续。", + "reload": "重新加载", "success": "成功" } diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 799f35d..21f6ac2 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -3,10 +3,13 @@ import './assets/theme.css' import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import App from './App' +import AppErrorBoundary from './components/common/AppErrorBoundary' import './i18n' createRoot(document.getElementById('root')!).render( - + + + )