refactor: 提取 useToast hook + Toast 组件,消除 4 处重复的通知逻辑

将 state + setTimeout 自动消失逻辑封装为 useToast hook,
Toast UI 统一为组件支持 top/bottom 两种位置,净减约 12 行。
This commit is contained in:
2026-02-26 17:50:28 +08:00
parent 948274bcb9
commit b98920858c
6 changed files with 86 additions and 98 deletions
+10 -25
View File
@@ -24,6 +24,8 @@ import {
Heart,
} from "lucide-react";
import EmptyState from "@/components/EmptyState";
import Toast from "@/components/Toast";
import { useToast } from "@/hooks/useToast";
import RestaurantImage from "@/components/RestaurantImage";
import { ProfileCardSkeleton, RecordItemSkeleton } from "@/components/Skeleton";
import { getUserId, getCachedProfile, setCachedProfile, setCachedPreferences, logout } from "@/lib/userId";
@@ -68,12 +70,7 @@ export default function ProfilePage() {
const [showHistory, setShowHistory] = useState(true);
const [showFavorites, setShowFavorites] = useState(true);
const [toast, setToast] = useState("");
const showToast = useCallback((msg: string) => {
setToast(msg);
setTimeout(() => setToast(""), 2200);
}, []);
const toast = useToast();
useEffect(() => {
const cached = getCachedProfile();
@@ -141,7 +138,7 @@ export default function ProfilePage() {
setProfile((prev) => prev ? { ...prev, username: trimmed } : prev);
setCachedProfile({ id: userId, username: trimmed, avatar: profile!.avatar });
setEditingUsername(false);
showToast("用户名已更新");
toast.show("用户名已更新");
} else {
setUsernameMsg(data.error ?? "更新失败");
}
@@ -180,7 +177,7 @@ export default function ProfilePage() {
setCurrentPassword("");
setNewPassword("");
setConfirmPassword("");
showToast("密码已更新");
toast.show("密码已更新");
} else {
setPasswordMsg(data.error ?? "更新失败");
}
@@ -202,10 +199,10 @@ export default function ProfilePage() {
setProfile((prev) => prev ? { ...prev, avatar: emoji } : prev);
setCachedProfile({ id: userId, username: profile!.username, avatar: emoji });
setEditingAvatar(false);
showToast("头像已更新");
toast.show("头像已更新");
}
} catch {
showToast("更新失败");
toast.show("更新失败");
}
};
@@ -244,9 +241,9 @@ export default function ProfilePage() {
body: JSON.stringify({ userId, favoriteId: favId }),
});
setFavorites((f) => f.filter((x) => x.id !== favId));
showToast("已取消收藏");
toast.show("已取消收藏");
} catch {
showToast("操作失败");
toast.show("操作失败");
}
};
@@ -714,19 +711,7 @@ export default function ProfilePage() {
</motion.div>
</div>
<AnimatePresence>
{toast && (
<motion.div
className="fixed left-1/2 top-10 z-60 -translate-x-1/2 rounded-xl bg-elevated px-4 py-2.5 text-xs font-medium text-heading shadow-lg ring-1 ring-subtle"
initial={{ opacity: 0, y: -12, x: "-50%" }}
animate={{ opacity: 1, y: 0, x: "-50%" }}
exit={{ opacity: 0, y: -12, x: "-50%" }}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
>
{toast}
</motion.div>
)}
</AnimatePresence>
<Toast message={toast.message} />
</div>
);
}