Skip to content

Commit 6792aa5

Browse files
committed
fix: 修复水合错误
1 parent da56121 commit 6792aa5

3 files changed

Lines changed: 24 additions & 10 deletions

File tree

app/components/Header.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import { LiveEditionLabel } from "./LiveEditionLabel";
1010
export function Header() {
1111
const now = new Date();
1212
const editionTimestampMs = now.getTime();
13+
// 使用 UTC 时区格式化日期,保证服务端渲染结果与客户端一致
1314
const formattedDate = now.toLocaleDateString("en-US", {
1415
month: "long",
1516
day: "numeric",
1617
year: "numeric",
18+
timeZone: "Australia/Sydney",
1719
});
1820
return (
1921
<header className="fixed top-0 w-full z-50 bg-[var(--background)] border-b border-[var(--foreground)] py-2 transition-colors duration-300">

app/components/LiveEditionLabel.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,17 @@ export function LiveEditionLabel({ initialTimestamp }: LiveEditionLabelProps) {
3434
return () => clearInterval(intervalId);
3535
}, []);
3636

37-
const editionDay = currentDate.getDate();
38-
const editionMonth = currentDate.toLocaleString("en-US", { month: "long" });
37+
// 初次渲染(水合阶段)使用固定时区(悉尼),保证服务端/客户端一致,
38+
// 避免时区差异导致 React error #418(hydration mismatch)。
39+
// 挂载后也继续使用悉尼时区保持一致性。
40+
const TIMEZONE = "Australia/Sydney";
41+
const editionDay = Number(
42+
currentDate.toLocaleString("en-US", { day: "numeric", timeZone: TIMEZONE }),
43+
);
44+
const editionMonth = currentDate.toLocaleString("en-US", {
45+
month: "long",
46+
timeZone: TIMEZONE,
47+
});
3948

4049
return (
4150
<>

app/components/ThemeProvider.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,22 @@ export function ThemeProvider({
2727
storageKey = "vite-ui-theme",
2828
...props
2929
}: ThemeProviderProps) {
30-
const [theme, setTheme] = useState<Theme>(() => {
31-
if (typeof window === "undefined") {
32-
return defaultTheme;
33-
}
30+
// 初始状态始终使用 defaultTheme,保证服务端/客户端一致,
31+
// 避免 localStorage 读取导致水合不匹配。
32+
// layout.tsx 中的内联脚本已确保首屏无闪烁(在 React 水合前设置了 CSS class)。
33+
const [theme, setTheme] = useState<Theme>(defaultTheme);
3434

35+
// 挂载后从 localStorage 读取用户之前保存的主题
36+
useEffect(() => {
3537
try {
3638
const stored = localStorage.getItem(storageKey) as Theme | null;
37-
return stored ?? defaultTheme;
39+
if (stored) {
40+
setTheme(stored);
41+
}
3842
} catch {
39-
console.error("Error reading theme from localStorage");
40-
return defaultTheme;
43+
// 忽略 localStorage 访问错误
4144
}
42-
});
45+
}, [storageKey]);
4346

4447
useEffect(() => {
4548
const root = window.document.documentElement;

0 commit comments

Comments
 (0)