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

Loading…
Cancel
Save