Browse Source

refactor: simplify ErrorBoundary, remove over-engineering

Remove speculative additions that didn't address the root cause:
- isNetworkRelatedError: assuming all render errors are chunk errors
  when offline masks genuine bugs
- CHUNK_ERROR_PATTERNS const and error.stack scanning: the original
  isChunkLoadError already matches utoopack's ChunkLoadError format
- getTitleId/getSubTitleId helpers: single-call-site abstractions
- Card wrapper: unnecessary inside ProLayout content area
- Offline-aware handleRetry: chunk errors should be the only trigger
  for reload, not any error while offline

Kept the real improvements:
- Correct 3-way defaultMessage for subtitle (offline chunk / online
  chunk / render error)
- Auto-reload on network recovery when in error state
- ProLayout ErrorBoundary prop (the actual fix in previous commit)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pull/11756/head
afc163 3 weeks ago
parent
commit
c8b5a26161
  1. 119
      src/components/ErrorBoundary/index.tsx

119
src/components/ErrorBoundary/index.tsx

@ -1,34 +1,13 @@
import { getIntl } from '@umijs/max';
import { Button, Card, Result } from 'antd';
import { Button, Result } from 'antd';
import React from 'react';
const CHUNK_ERROR_PATTERNS = [
/(?:loading|failed to load) (?:css )?chunk/i,
/imported module/i,
/chunkloaderror/i,
];
function isChunkLoadError(error: Error): boolean {
if (error.name === 'ChunkLoadError') return true;
// Check both message and stack trace (React may wrap errors)
const text = `${error.message}\n${error.stack ?? ''}`;
return CHUNK_ERROR_PATTERNS.some((p) => p.test(text));
}
/** When offline, any render error is likely a chunk/network failure. */
function isNetworkRelatedError(error: Error, offline: boolean): boolean {
return offline || isChunkLoadError(error);
}
function getTitleId(networkRelated: boolean): string {
return networkRelated ? 'app.error.chunk.title' : 'app.error.render.title';
}
function getSubTitleId(networkRelated: boolean, offline: boolean): string {
if (!networkRelated) return 'app.error.render.description';
return offline
? 'app.error.chunk.description.offline'
: 'app.error.chunk.description.online';
return (
error.name === 'ChunkLoadError' ||
/(?:loading|failed to load) (?:css )?chunk/i.test(error.message) ||
/imported module/i.test(error.message)
);
}
interface ErrorBoundaryState {
@ -63,10 +42,7 @@ export default class ErrorBoundary extends React.Component<
handleOnline = () => {
this.setState({ isOnline: true });
// Auto-reload when coming back online if the error was network-related
if (this.state.hasError && this.state.error) {
window.location.reload();
}
if (this.state.hasError) window.location.reload();
};
handleOffline = () => this.setState({ isOnline: false });
@ -76,11 +52,7 @@ export default class ErrorBoundary extends React.Component<
}
handleRetry = () => {
// For network-related errors, always reload (re-setting state won't re-fetch the chunk)
if (
this.state.error &&
isNetworkRelatedError(this.state.error, !this.state.isOnline)
) {
if (this.state.error && isChunkLoadError(this.state.error)) {
window.location.reload();
} else {
this.setState({ hasError: false, error: null });
@ -95,46 +67,47 @@ export default class ErrorBoundary extends React.Component<
const { error } = this.state;
const intl = getIntl();
const isOffline = !this.state.isOnline;
const networkRelated = isNetworkRelatedError(error, isOffline);
const isChunkError = isChunkLoadError(error);
const title = intl.formatMessage({
id: getTitleId(networkRelated),
defaultMessage: networkRelated
? 'Failed to load page'
: 'Something went wrong',
});
const subTitle = intl.formatMessage({
id: getSubTitleId(networkRelated, isOffline),
defaultMessage:
networkRelated && isOffline
? 'Your network connection has been lost. Please check your connection and refresh.'
: networkRelated
? 'Page resources failed to load. Please refresh and try again.'
: 'Sorry, an error occurred on this page. Please refresh or go back to the home page.',
});
const subTitleId = isChunkError
? isOffline
? 'app.error.chunk.description.offline'
: 'app.error.chunk.description.online'
: 'app.error.render.description';
return (
<Card variant="borderless">
<Result
status="error"
title={title}
subTitle={subTitle}
extra={[
<Button type="primary" key="retry" onClick={this.handleRetry}>
{intl.formatMessage({
id: 'app.error.retry',
defaultMessage: 'Refresh',
})}
</Button>,
<Button href="/" key="home">
{intl.formatMessage({
id: 'app.error.home',
defaultMessage: 'Back Home',
})}
</Button>,
]}
/>
</Card>
<Result
status="error"
title={intl.formatMessage({
id: isChunkError ? 'app.error.chunk.title' : 'app.error.render.title',
defaultMessage: isChunkError
? 'Failed to load page'
: 'Something went wrong',
})}
subTitle={intl.formatMessage({
id: subTitleId,
defaultMessage:
isChunkError && isOffline
? 'Your network connection has been lost. Please check your connection and refresh.'
: isChunkError
? 'Page resources failed to load. Please refresh and try again.'
: 'Sorry, an error occurred on this page. Please refresh or go back to the home page.',
})}
extra={[
<Button type="primary" key="retry" onClick={this.handleRetry}>
{intl.formatMessage({
id: 'app.error.retry',
defaultMessage: 'Refresh',
})}
</Button>,
<Button href="/" key="home">
{intl.formatMessage({
id: 'app.error.home',
defaultMessage: 'Back Home',
})}
</Button>,
]}
/>
);
}
}

Loading…
Cancel
Save