Browse Source

fix: remove non-functional soft retry and simplify ErrorBoundary

- Remove removeFailedChunkScripts() — nothing marks scripts with
  data-failed and the bundler caches rejected promises, so soft retry
  was a no-op that misleads users
- Remove Retry button; keep Reload Page + Back Home for all errors
- Remove unused app.error.retry locale key from all 8 locales
- Evaluate navigator.onLine directly in render instead of caching in
  instance field, so error UI always reflects current network status
- Limit handleOnline auto-reload to chunk load errors only, preventing
  data loss from indiscriminate reload on render errors
- Remove offline event listener (no longer needed without isOnline cache)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pull/11756/head
afc163 3 weeks ago
parent
commit
b27643b3ca
  1. 75
      src/components/ErrorBoundary/index.tsx
  2. 1
      src/locales/bn-BD/pwa.ts
  3. 1
      src/locales/en-US/pwa.ts
  4. 1
      src/locales/fa-IR/pwa.ts
  5. 1
      src/locales/id-ID/pwa.ts
  6. 1
      src/locales/ja-JP/pwa.ts
  7. 1
      src/locales/pt-BR/pwa.ts
  8. 1
      src/locales/zh-CN/pwa.ts
  9. 1
      src/locales/zh-TW/pwa.ts

75
src/components/ErrorBoundary/index.tsx

@ -17,40 +17,11 @@ function getSubTitleId(isChunkError: boolean, isOffline: boolean): string {
: 'app.error.chunk.description.online'; : 'app.error.chunk.description.online';
} }
/** function renderErrorFallback(error: Error, onReload: () => void) {
* Remove failed chunk script/link tags so the bundler runtime can retry loading.
* Utoopack caches failed chunks as rejected promises; removing the DOM elements
* and clearing its internal cache isn't possible via public API, but removing
* the script tags gives us a clean slate on the next retry.
*/
function removeFailedChunkScripts() {
const scripts = document.querySelectorAll(
'script[src][data-failed], link[href][data-failed]',
);
for (const el of scripts) el.remove();
}
function renderErrorFallback(
error: Error,
isOnline: boolean,
onReload: () => void,
onRetry: () => void,
) {
const intl = getIntl(); const intl = getIntl();
const isOffline = !isOnline; const isOffline = !navigator.onLine;
const isChunkError = isChunkLoadError(error); const isChunkError = isChunkLoadError(error);
const subTitleId = getSubTitleId(isChunkError, isOffline);
const retryButton = isChunkError ? (
<Button type="primary" key="retry" onClick={onRetry}>
{intl.formatMessage({
id: 'app.error.retry',
defaultMessage: 'Retry',
})}
</Button>
) : null;
return ( return (
<div style={{ padding: 24 }}> <div style={{ padding: 24 }}>
<Card variant="borderless"> <Card variant="borderless">
@ -65,7 +36,7 @@ function renderErrorFallback(
: 'Something went wrong', : 'Something went wrong',
})} })}
subTitle={intl.formatMessage({ subTitle={intl.formatMessage({
id: subTitleId, id: getSubTitleId(isChunkError, isOffline),
defaultMessage: defaultMessage:
isChunkError && isOffline isChunkError && isOffline
? 'Your network connection has been lost. Please check your connection and reload.' ? 'Your network connection has been lost. Please check your connection and reload.'
@ -74,12 +45,7 @@ function renderErrorFallback(
: 'Sorry, an error occurred on this page. Please reload or go back to the home page.', : 'Sorry, an error occurred on this page. Please reload or go back to the home page.',
})} })}
extra={[ extra={[
retryButton, <Button type="primary" key="reload" onClick={onReload}>
<Button
type={isChunkError ? 'default' : 'primary'}
key="reload"
onClick={onReload}
>
{intl.formatMessage({ {intl.formatMessage({
id: 'app.error.reload', id: 'app.error.reload',
defaultMessage: 'Reload Page', defaultMessage: 'Reload Page',
@ -91,7 +57,7 @@ function renderErrorFallback(
defaultMessage: 'Back Home', defaultMessage: 'Back Home',
})} })}
</Button>, </Button>,
].filter(Boolean)} ]}
/> />
</Card> </Card>
</div> </div>
@ -112,7 +78,6 @@ export default class ErrorBoundary extends React.Component<
ErrorBoundaryState ErrorBoundaryState
> { > {
state: ErrorBoundaryState = { hasError: false, error: null }; state: ErrorBoundaryState = { hasError: false, error: null };
private isOnline = typeof navigator !== 'undefined' ? navigator.onLine : true;
static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> { static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {
return { hasError: true, error }; return { hasError: true, error };
@ -120,45 +85,33 @@ export default class ErrorBoundary extends React.Component<
componentDidMount() { componentDidMount() {
window.addEventListener('online', this.handleOnline); window.addEventListener('online', this.handleOnline);
window.addEventListener('offline', this.handleOffline);
} }
componentWillUnmount() { componentWillUnmount() {
window.removeEventListener('online', this.handleOnline); window.removeEventListener('online', this.handleOnline);
window.removeEventListener('offline', this.handleOffline);
} }
/** Auto-reload when coming back online, but only for chunk load errors. */
handleOnline = () => { handleOnline = () => {
this.isOnline = true; if (
if (this.state.hasError) window.location.reload(); this.state.hasError &&
}; this.state.error &&
isChunkLoadError(this.state.error)
handleOffline = () => { ) {
this.isOnline = false; window.location.reload();
}
}; };
componentDidCatch(error: Error, info: React.ErrorInfo) { componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error('[ErrorBoundary]', error, info.componentStack); console.error('[ErrorBoundary]', error, info.componentStack);
} }
/** Soft retry: clear failed scripts, reset state, let React re-render. */
handleRetry = () => {
removeFailedChunkScripts();
this.setState({ hasError: false, error: null });
};
/** Hard reload: full page refresh. */
handleReload = () => { handleReload = () => {
window.location.reload(); window.location.reload();
}; };
render() { render() {
if (!this.state.hasError || !this.state.error) return this.props.children; if (!this.state.hasError || !this.state.error) return this.props.children;
return renderErrorFallback( return renderErrorFallback(this.state.error, this.handleReload);
this.state.error,
this.isOnline,
this.handleReload,
this.handleRetry,
);
} }
} }

1
src/locales/bn-BD/pwa.ts

@ -8,7 +8,6 @@ export default {
'app.error.render.title': 'কিছু ভুল হয়েছে', 'app.error.render.title': 'কিছু ভুল হয়েছে',
'app.error.render.description': 'app.error.render.description':
'দুঃখিত, এই পৃষ্ঠায় একটি ত্রুটি ঘটেছে। দয়া করে রিফ্রেশ করুন বা হোম পৃষ্ঠায় ফিরে যান।', 'দুঃখিত, এই পৃষ্ঠায় একটি ত্রুটি ঘটেছে। দয়া করে রিফ্রেশ করুন বা হোম পৃষ্ঠায় ফিরে যান।',
'app.error.retry': 'আবার চেষ্টা করুন',
'app.error.reload': 'পৃষ্ঠা রিফ্রেশ করুন', 'app.error.reload': 'পৃষ্ঠা রিফ্রেশ করুন',
'app.error.home': 'হোমে ফিরে যান', 'app.error.home': 'হোমে ফিরে যান',
'app.request.offline': 'app.request.offline':

1
src/locales/en-US/pwa.ts

@ -9,7 +9,6 @@ export default {
'app.error.render.title': 'Something went wrong', 'app.error.render.title': 'Something went wrong',
'app.error.render.description': 'app.error.render.description':
'Sorry, an error occurred on this page. Please reload or go back to the home page.', 'Sorry, an error occurred on this page. Please reload or go back to the home page.',
'app.error.retry': 'Retry',
'app.error.reload': 'Reload Page', 'app.error.reload': 'Reload Page',
'app.error.home': 'Back Home', 'app.error.home': 'Back Home',
'app.request.offline': 'app.request.offline':

1
src/locales/fa-IR/pwa.ts

@ -9,7 +9,6 @@ export default {
'app.error.render.title': 'خطایی رخ داد', 'app.error.render.title': 'خطایی رخ داد',
'app.error.render.description': 'app.error.render.description':
'متأسفانه، در این صفحه خطایی رخ داد. لطفاً صفحه را بارگذاری مجدد کنید یا به صفحه اصلی بازگردید.', 'متأسفانه، در این صفحه خطایی رخ داد. لطفاً صفحه را بارگذاری مجدد کنید یا به صفحه اصلی بازگردید.',
'app.error.retry': 'تلاش مجدد',
'app.error.reload': 'بارگذاری مجدد صفحه', 'app.error.reload': 'بارگذاری مجدد صفحه',
'app.error.home': 'بازگشت به صفحه اصلی', 'app.error.home': 'بازگشت به صفحه اصلی',
'app.request.offline': 'app.request.offline':

1
src/locales/id-ID/pwa.ts

@ -9,7 +9,6 @@ export default {
'app.error.render.title': 'Terjadi kesalahan', 'app.error.render.title': 'Terjadi kesalahan',
'app.error.render.description': 'app.error.render.description':
'Maaf, terjadi kesalahan pada halaman ini. Muat ulang halaman atau kembali ke beranda.', 'Maaf, terjadi kesalahan pada halaman ini. Muat ulang halaman atau kembali ke beranda.',
'app.error.retry': 'Coba Lagi',
'app.error.reload': 'Muat Ulang Halaman', 'app.error.reload': 'Muat Ulang Halaman',
'app.error.home': 'Kembali ke Beranda', 'app.error.home': 'Kembali ke Beranda',
'app.request.offline': 'app.request.offline':

1
src/locales/ja-JP/pwa.ts

@ -9,7 +9,6 @@ export default {
'app.error.render.title': 'エラーが発生しました', 'app.error.render.title': 'エラーが発生しました',
'app.error.render.description': 'app.error.render.description':
'申し訳ありません、ページでエラーが発生しました。ページを再読み込みするか、ホームに戻ってください。', '申し訳ありません、ページでエラーが発生しました。ページを再読み込みするか、ホームに戻ってください。',
'app.error.retry': '再試行',
'app.error.reload': 'ページを再読み込み', 'app.error.reload': 'ページを再読み込み',
'app.error.home': 'ホームに戻る', 'app.error.home': 'ホームに戻る',
'app.request.offline': 'app.request.offline':

1
src/locales/pt-BR/pwa.ts

@ -9,7 +9,6 @@ export default {
'app.error.render.title': 'Algo deu errado', 'app.error.render.title': 'Algo deu errado',
'app.error.render.description': 'app.error.render.description':
'Desculpe, ocorreu um erro nesta página. Atualize a página ou volte para a página inicial.', 'Desculpe, ocorreu um erro nesta página. Atualize a página ou volte para a página inicial.',
'app.error.retry': 'Tentar novamente',
'app.error.reload': 'Atualizar página', 'app.error.reload': 'Atualizar página',
'app.error.home': 'Voltar ao Início', 'app.error.home': 'Voltar ao Início',
'app.request.offline': 'app.request.offline':

1
src/locales/zh-CN/pwa.ts

@ -7,7 +7,6 @@ export default {
'app.error.render.title': '页面出现错误', 'app.error.render.title': '页面出现错误',
'app.error.render.description': 'app.error.render.description':
'抱歉,页面遇到了一些问题,请刷新页面或返回首页。', '抱歉,页面遇到了一些问题,请刷新页面或返回首页。',
'app.error.retry': '重新加载',
'app.error.reload': '刷新页面', 'app.error.reload': '刷新页面',
'app.error.home': '返回首页', 'app.error.home': '返回首页',
'app.request.offline': '网络不可用,请检查网络连接后重试。', 'app.request.offline': '网络不可用,请检查网络连接后重试。',

1
src/locales/zh-TW/pwa.ts

@ -8,7 +8,6 @@ export default {
'app.error.render.title': '頁面出現錯誤', 'app.error.render.title': '頁面出現錯誤',
'app.error.render.description': 'app.error.render.description':
'抱歉,頁面遇到了一些問題,請重新整理頁面或返回首頁。', '抱歉,頁面遇到了一些問題,請重新整理頁面或返回首頁。',
'app.error.retry': '重試',
'app.error.reload': '重新整理頁面', 'app.error.reload': '重新整理頁面',
'app.error.home': '返回首頁', 'app.error.home': '返回首頁',
'app.request.offline': '網路不可用,請檢查網路連線後重試。', 'app.request.offline': '網路不可用,請檢查網路連線後重試。',

Loading…
Cancel
Save