From bf06b61f109795a73c2be77a57e0d78576df8426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Fri, 19 Jun 2026 21:00:27 +0800 Subject: [PATCH] =?UTF-8?q?fix(RouterProvider):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=89=8D=E4=B8=8D=E8=A6=86=E7=9B=96=20chrome.storage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 isLoaded 的初始状态设置为 false,避免在加载初始数据前错误地覆盖存储。 - 新增测试用例,验证在 localStorage 快照过期时,loadInitialData 完成前不应覆盖 chrome.storage 的行为。 --- src/providers/RouterProvider.tsx | 7 +-- .../__tests__/RouterProvider.test.tsx | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/providers/RouterProvider.tsx b/src/providers/RouterProvider.tsx index 2470ce3..81263b5 100644 --- a/src/providers/RouterProvider.tsx +++ b/src/providers/RouterProvider.tsx @@ -104,12 +104,7 @@ export function RouterProvider({ getSyncSnapshot('app/recentlyUsedTools', [], isValidPageList), ); - const [isLoaded, setIsLoaded] = useState(() => { - const snapshotKey = localStorage.getItem(`snapshot/${syncKey as string}`); - const snapshotVisible = localStorage.getItem(`snapshot/${visiblePagesKey as string}`); - const snapshotOrder = localStorage.getItem(`snapshot/${pageOrderKey as string}`); - return !!(snapshotKey && snapshotVisible && snapshotOrder); - }); + const [isLoaded, setIsLoaded] = useState(false); /** * 从异步存储中安全溯源初始数据 diff --git a/src/providers/__tests__/RouterProvider.test.tsx b/src/providers/__tests__/RouterProvider.test.tsx index 76fda0d..b71640f 100644 --- a/src/providers/__tests__/RouterProvider.test.tsx +++ b/src/providers/__tests__/RouterProvider.test.tsx @@ -277,6 +277,52 @@ describe('RouterProvider', () => { }); }); + it('localStorage 快照过期时,loadInitialData 完成前不应覆盖 chrome.storage', async () => { + const staleRoute = 'timestamp'; + const correctRoute = 'jsonTools'; + const defaultVisible = ['dashboard', 'timestamp', 'storageCleaner']; + const defaultOrder = ['timestamp', 'storageCleaner']; + + localStorage.setItem('snapshot/app/currentRoute', JSON.stringify(staleRoute)); + localStorage.setItem('snapshot/app/visiblePages', JSON.stringify(defaultVisible)); + localStorage.setItem('snapshot/app/pageOrder', JSON.stringify(defaultOrder)); + + const storage = new Map([ + ['app/currentRoute', correctRoute], + ['app/visiblePages', defaultVisible], + ['app/pageOrder', defaultOrder], + ['app/recentlyUsedTools', []], + ]); + + let resolveGet: () => void; + const getBlocked = new Promise((resolve) => { + resolveGet = resolve; + }); + + (storageUtil.get as any).mockImplementation(async (key: string, defaultValue: unknown) => { + await getBlocked; + return storage.get(key) ?? defaultValue; + }); + (storageUtil.set as any).mockImplementation(async (key: string, value: unknown) => { + storage.set(key, value); + }); + + render( + + + , + ); + + expect(screen.getByTestId('current-page')).toHaveTextContent(staleRoute); + expect(storageUtil.set).not.toHaveBeenCalledWith('app/currentRoute', staleRoute); + + resolveGet!(); + await waitFor(() => { + expect(screen.getByTestId('current-page')).toHaveTextContent(correctRoute); + expect(storage.get('app/currentRoute')).toBe(correctRoute); + }); + }); + it('组件卸载时不应设置 isLoaded 状态(竞态条件防护)', async () => { let resolveStorage: (value: unknown) => void; const storagePromise = new Promise((resolve) => {