fix(web): retry rejected route modules

This commit is contained in:
lingniu
2026-07-16 07:01:34 +08:00
parent ec3071d59b
commit fa886abff9
4 changed files with 61 additions and 25 deletions

View File

@@ -1,5 +1,5 @@
import { IconAlertTriangle, IconHome, IconRefresh } from '@douyinfe/semi-icons';
import { Component, type ElementType, type ErrorInfo, type ReactNode, Suspense } from 'react';
import { Component, type ElementType, type ErrorInfo, type ReactNode, Suspense, useCallback, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { PageLoading } from '../shared/AsyncState';
@@ -32,7 +32,7 @@ function rememberAutoReload(routeKey: string) {
}
}
class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey: string }, { error?: Error }> {
class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey: string; retryKey: number; onRetry: () => void }, { error?: Error }> {
state: { error?: Error } = {};
static getDerivedStateFromError(error: Error) {
@@ -46,8 +46,8 @@ class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey
}
}
componentDidUpdate(previous: Readonly<{ children: ReactNode; routeKey: string }>) {
if (this.state.error && previous.routeKey !== this.props.routeKey) {
componentDidUpdate(previous: Readonly<{ children: ReactNode; routeKey: string; retryKey: number; onRetry: () => void }>) {
if (this.state.error && (previous.routeKey !== this.props.routeKey || previous.retryKey !== this.props.retryKey)) {
this.setState({ error: undefined });
}
}
@@ -65,6 +65,7 @@ class RecoverableRouteBoundary extends Component<{ children: ReactNode; routeKey
<p>{chunkTimeout ? '系统已等待并尝试恢复,可能是网络暂时不稳定。刷新后会重新加载当前模块,不会丢失服务端数据。' : chunkError ? '通常是发布后旧标签页仍引用上一版本资源。刷新后会恢复,不会丢失服务端数据。' : '页面已被安全隔离,侧栏和其他模块仍可使用。更换筛选条件会自动重试,也可刷新当前页面。'}</p>
<code>{error.message || error.name}</code>
<footer>
<button type="button" onClick={this.props.onRetry}><IconRefresh /></button>
<button type="button" onClick={() => window.location.reload()}><IconRefresh /></button>
<a href="/monitor"><IconHome /></a>
</footer>
@@ -99,10 +100,13 @@ export class PlatformErrorBoundary extends Component<{ children: ReactNode }, {
}
}
export function RoutePage({ page: Page, label }: { page: ElementType; label: string }) {
export function RoutePage({ page, recreatePage, label }: { page: ElementType; recreatePage?: () => ElementType; label: string }) {
const location = useLocation();
const routeKey = `${location.pathname}${location.search}`;
return <RecoverableRouteBoundary key={location.pathname} routeKey={routeKey}>
<Suspense fallback={<PageLoading label={`正在加载${label}`} />}><Page /></Suspense>
const [retryKey, setRetryKey] = useState(0);
const Page = useMemo(() => retryKey && recreatePage ? recreatePage() : page, [page, recreatePage, retryKey]);
const retry = useCallback(() => setRetryKey((value) => value + 1), []);
return <RecoverableRouteBoundary key={location.pathname} routeKey={routeKey} retryKey={retryKey} onRetry={retry}>
<Suspense fallback={<PageLoading label={`正在加载${label}`} />}><Page key={retryKey} /></Suspense>
</RecoverableRouteBoundary>;
}