Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cold-start-multi-hydration-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@app/ratewise': patch
---

修復冷啟動時多幣別換算模式無法還原的問題(生產環境 persist 已同步但 hydration 回呼未觸發)。
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
markRestoreAttempted,
shouldRestoreToMulti,
isPersistedMultiPendingStoreSync,
readPersistedLastConverterView,
} from './coldStartRestore';

/**
Expand All @@ -20,14 +21,46 @@ export function RememberedHomeRoute() {
const [hydrated, setHydrated] = useState(isTestEnv);

useEffect(() => {
let active = true;
const markHydrated = () => {
if (active) {
setHydrated(true);
}
};

const isStoreSyncedWithPersist = (): boolean => {
const persisted = readPersistedLastConverterView();
if (persisted === null) return true;
return useConverterStore.getState().lastConverterView === persisted;
};

if (useConverterStore.persist.hasHydrated()) {
// eslint-disable-next-line react-hooks/set-state-in-effect -- persist hydration marker
setHydrated(true);
return undefined;
markHydrated();
return () => {
active = false;
};
}
return useConverterStore.persist.onFinishHydration(() => {
setHydrated(true);

const unsubFinish = useConverterStore.persist.onFinishHydration(markHydrated);

// SSG/CSR:store 可能已 merge persist,但 onFinishHydration 未觸發。
queueMicrotask(() => {
if (isStoreSyncedWithPersist()) {
markHydrated();
}
});

const unsubStore = useConverterStore.subscribe((state, prevState) => {
if (state.lastConverterView !== prevState.lastConverterView && isStoreSyncedWithPersist()) {
markHydrated();
}
});

return () => {
active = false;
unsubFinish();
unsubStore();
};
}, []);

const hasDeepLink =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,18 @@ describe('RememberedHomeRoute', () => {
expect(await screen.findByTestId('ratewise-single')).toBeInTheDocument();
expect(screen.queryByTestId('multi-page')).not.toBeInTheDocument();
});

it('production MODE:store 已同步 multi 但 hasHydrated=false 時仍導向 /multi', async () => {
vi.stubEnv('MODE', 'production');
localStorage.setItem(CONVERTER_STORE_KEY, multiPersistPayload);
useConverterStore.setState({ lastConverterView: 'multi' });
vi.spyOn(useConverterStore.persist, 'hasHydrated').mockReturnValue(false);

renderHome('/');

await waitFor(() => {
expect(screen.getByTestId('multi-page')).toBeInTheDocument();
});
vi.unstubAllEnvs();
});
});
7 changes: 6 additions & 1 deletion docs/dev/002_development_reward_penalty_log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> 版本:outline-v2-ultra
> 原則:每筆只保留日期、ID、原因、解法。
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+77
> 本次分數變化:+1(reward 1、penalty 0、neutral 0)|累計總分:+78

## 新增模板(4 行)

Expand All @@ -13,6 +13,11 @@

## 條目(新→舊)

- 日期:2026-06-27
- ID:reward-cold-start-multi-hydration-fallback
- 原因:RememberedHomeRoute 僅依 hasHydrated/onFinishHydration 設 hydrated,SSG/CSR 生產環境 store 已 merge persist 但回呼未觸發,multi 冷啟動無法還原
- 解法:hydration effect 補 queueMicrotask 與 store subscribe fallback,store 與 localStorage 同步即標記 hydrated

- 日期:2026-06-27
- ID:reward-applayout-logo-vitest-teardown-flake
- 原因:AppLayout.logo.test 渲染後 lazy prompt 非同步觸發 console,vitest onConsoleLog RPC 在 worker 關閉時仍 pending 導致 EnvironmentTeardownError
Expand Down
Loading