mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 02:43:35 +08:00
fix: ignore stale data center requests
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type React from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Database, RefreshCw, Settings2, TriangleAlert } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { dataCapabilityApi } from '../api/dataCapability';
|
||||
@@ -54,26 +54,34 @@ const DataCenterPage: React.FC = () => {
|
||||
const [overview, setOverview] = useState<DataCapabilityOverview | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
const requestGeneration = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${t('dataCenter.title')} - DSA`;
|
||||
}, [t]);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
const generation = ++requestGeneration.current;
|
||||
setLoading(true);
|
||||
setError('');
|
||||
try {
|
||||
setOverview(await dataCapabilityApi.getOverview());
|
||||
const nextOverview = await dataCapabilityApi.getOverview();
|
||||
if (generation !== requestGeneration.current) return;
|
||||
setOverview(nextOverview);
|
||||
} catch {
|
||||
if (generation !== requestGeneration.current) return;
|
||||
setOverview(null);
|
||||
setError(t('dataCenter.loadError'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (generation === requestGeneration.current) setLoading(false);
|
||||
}
|
||||
}, [t]);
|
||||
|
||||
useEffect(() => {
|
||||
void load();
|
||||
return () => {
|
||||
requestGeneration.current += 1;
|
||||
};
|
||||
}, [load]);
|
||||
|
||||
const summary = useMemo(() => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { UiLanguageProvider, useUiLanguage } from '../../contexts/UiLanguageContext';
|
||||
import DataCenterPage from '../DataCenterPage';
|
||||
|
||||
const { getOverview } = vi.hoisted(() => ({ getOverview: vi.fn() }));
|
||||
@@ -93,6 +94,21 @@ const overview = {
|
||||
warnings: ['screening_health_unknown'],
|
||||
};
|
||||
|
||||
function LanguageSwitch() {
|
||||
const { language, setLanguage } = useUiLanguage();
|
||||
return <button onClick={() => setLanguage(language === 'zh' ? 'en' : 'zh')}>switch language</button>;
|
||||
}
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (reason?: unknown) => void;
|
||||
const promise = new Promise<T>((resolvePromise, rejectPromise) => {
|
||||
resolve = resolvePromise;
|
||||
reject = rejectPromise;
|
||||
});
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
getOverview.mockResolvedValue(overview);
|
||||
@@ -137,4 +153,28 @@ describe('DataCenterPage', () => {
|
||||
expect(await screen.findByText('暂无能力数据')).toBeInTheDocument();
|
||||
await waitFor(() => expect(getOverview).toHaveBeenCalledTimes(2));
|
||||
});
|
||||
|
||||
it('ignores an older request that finishes after a language-triggered reload', async () => {
|
||||
const first = deferred<typeof overview>();
|
||||
const second = deferred<typeof overview>();
|
||||
getOverview.mockReset().mockReturnValueOnce(first.promise).mockReturnValueOnce(second.promise);
|
||||
|
||||
render(
|
||||
<UiLanguageProvider>
|
||||
<LanguageSwitch />
|
||||
<MemoryRouter><DataCenterPage /></MemoryRouter>
|
||||
</UiLanguageProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(getOverview).toHaveBeenCalledTimes(1));
|
||||
fireEvent.click(screen.getByRole('button', { name: 'switch language' }));
|
||||
await waitFor(() => expect(getOverview).toHaveBeenCalledTimes(2));
|
||||
|
||||
second.resolve(overview);
|
||||
expect(await screen.findByText('AkShare')).toBeInTheDocument();
|
||||
first.reject(new Error('stale failure'));
|
||||
|
||||
await waitFor(() => expect(screen.queryByText('Failed to load data overview')).not.toBeInTheDocument());
|
||||
expect(screen.getByText('AkShare')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user