Skip to content

Commit d658f5c

Browse files
committed
chore(settings): CR - 删 server 端死代码 / loading 结束 / 同步 theme / timer 清理
Copilot CR #278: - page.tsx 删除 getServerUser 死代码:token 存 localStorage 服务端读不到,注释误导 - SettingsForm: token 缺失时 setLoading(false) 结束骨架屏 + 提示 + 跳登录页 - SettingsForm: 加载到偏好后立刻 setTheme,让已保存 theme 与当前主题一致 - SettingsForm: 用 useRef 存 toast timer,新 toast/卸载时 clearTimeout,避免 setState on unmounted - SettingsForm: handleSave 的 token 缺失分支也给 toast + 跳登录,与加载逻辑一致
1 parent 2970c95 commit d658f5c

1 file changed

Lines changed: 35 additions & 5 deletions

File tree

app/settings/SettingsForm.tsx

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// 用户偏好设置表单(Client Component)
44
// 负责:拉取偏好数据、渲染编辑 UI、提交保存、同步 ThemeProvider
55

6-
import { useEffect, useState } from "react";
6+
import { useEffect, useRef, useState } from "react";
77
import { useRouter } from "next/navigation";
88
import { useAuth } from "@/lib/use-auth";
99
import { useTheme } from "@/app/components/ThemeProvider";
@@ -49,6 +49,8 @@ export function SettingsForm() {
4949
type: "success" | "error";
5050
msg: string;
5151
} | null>(null);
52+
// toast 定时器 ref:新 toast / 卸载时清掉旧 timer,避免 setState on unmounted
53+
const toastTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
5254

5355
// 未登录时重定向
5456
useEffect(() => {
@@ -61,7 +63,13 @@ export function SettingsForm() {
6163
useEffect(() => {
6264
if (status !== "authenticated") return;
6365
const token = getToken();
64-
if (!token) return;
66+
// token 缺失时立刻结束 loading 并提示 + 跳转,否则页面会卡在骨架屏
67+
if (!token) {
68+
setLoading(false);
69+
showToast("error", "登录态丢失,请重新登录");
70+
router.replace("/login?redirect=/settings");
71+
return;
72+
}
6573

6674
fetch("/api/user-center/preferences", {
6775
headers: { satoken: token },
@@ -72,23 +80,45 @@ export function SettingsForm() {
7280
})
7381
.then((body) => {
7482
if (body?.success && body?.data) {
75-
setPrefs({ ...DEFAULT_PREFS, ...body.data });
83+
const merged = { ...DEFAULT_PREFS, ...body.data };
84+
setPrefs(merged);
85+
// 加载出来的 theme 立即同步到 ThemeProvider,避免"已保存设置与当前主题不一致"
86+
setTheme(merged.theme);
7687
}
7788
})
7889
.catch(() => {
7990
showToast("error", "无法加载偏好设置,已显示默认值");
8091
})
8192
.finally(() => setLoading(false));
93+
// setTheme 是 ThemeProvider 提供的稳定引用,router 同理;这里依赖 status 变化触发
94+
// eslint-disable-next-line react-hooks/exhaustive-deps
8295
}, [status]);
8396

97+
// 组件卸载时清掉残留 toast timer
98+
useEffect(() => {
99+
return () => {
100+
if (toastTimerRef.current) {
101+
clearTimeout(toastTimerRef.current);
102+
}
103+
};
104+
}, []);
105+
84106
function showToast(type: "success" | "error", msg: string) {
107+
if (toastTimerRef.current) {
108+
clearTimeout(toastTimerRef.current);
109+
}
85110
setToast({ type, msg });
86-
setTimeout(() => setToast(null), 3000);
111+
toastTimerRef.current = setTimeout(() => setToast(null), 3000);
87112
}
88113

89114
async function handleSave() {
90115
const token = getToken();
91-
if (!token) return;
116+
// token 缺失时给明确反馈并跳转登录,而不是静默返回让用户摸不着头脑
117+
if (!token) {
118+
showToast("error", "登录态丢失,请重新登录后再保存");
119+
router.replace("/login?redirect=/settings");
120+
return;
121+
}
92122
setSaving(true);
93123
try {
94124
const res = await fetch("/api/user-center/preferences", {

0 commit comments

Comments
 (0)