useToast 曾是只写局部 state 的 stub,返回的 toasts 数组无任何页面渲染, 而全局仅挂载 sonner 的 <Toaster />,导致 31 个组件的操作提示全部隐形 (用户表现为"点击保存没反应",实际请求已成功到达后端)。 - use-toast.tsx: useToast 统一转发到 sonner,保持返回值结构兼容 - ProfilePage.tsx: 密码修改去掉静默 return,校验失败给出明确提示; 保存按钮改为始终可点,保证点击必有反馈 // Logan.Li
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
// useToast — 统一委托到全局 sonner <Toaster />,确保提示真实可见。// Logan.Li
|
|
//
|
|
// 历史问题:早期版本把 toast 存在 hook 内的局部 state 并返回 toasts 数组,
|
|
// 但没有任何页面渲染该数组(全局只挂载了 sonner 的 <Toaster />),
|
|
// 导致所有 useToast() 调用产生的提示都是隐形的(表现为"保存没反应")。
|
|
// 现统一转发到 sonner,保持返回值结构兼容既有调用方。// Logan.Li
|
|
|
|
import { useCallback } from 'react';
|
|
import { toast as sonnerToast } from 'sonner';
|
|
|
|
export interface ToastOptions {
|
|
title: string;
|
|
description?: string;
|
|
variant?: 'default' | 'destructive';
|
|
}
|
|
|
|
interface UseToastReturn {
|
|
toast: (opts: ToastOptions) => void;
|
|
toasts: never[];
|
|
dismiss: (id: string) => void;
|
|
}
|
|
|
|
export function useToast(): UseToastReturn {
|
|
const toast = useCallback((opts: ToastOptions) => {
|
|
const { title, description, variant } = opts;
|
|
if (variant === 'destructive') {
|
|
sonnerToast.error(title, { description });
|
|
} else {
|
|
sonnerToast.success(title, { description });
|
|
}
|
|
}, []);
|
|
|
|
// sonner 自带 dismiss 能力,此处保留空实现以兼容既有解构。// Logan.Li
|
|
const dismiss = useCallback(() => {}, []);
|
|
|
|
return { toast, toasts: [], dismiss };
|
|
}
|