修复(ui): 解决Toast通知不正常关闭的问题

- 修复Toast组件中鼠标悬停状态的延迟通知逻辑
- 重构ToastContext中的定时器管理机制,防止内存泄漏
- 优化Toast的hover状态同步,确保定时器正确暂停和恢复
- 增强cleanup机制,清理已删除Toast的残留定时器
- 解决Toast在指定时间后不自动关闭的问题

修复内容:
- Toast.tsx: 移除鼠标离开后的1秒延迟,立即通知状态变化
- ToastContext.tsx: 改进定时器生命周期管理和清理逻辑

验证:
- 构建成功通过
- 所有测试套件通过(34个测试)
- Toast现在能正确按时关闭,悬停暂停功能正常
This commit is contained in:
2025-12-22 21:23:26 +08:00
parent 8d31b98736
commit 62946be82f
2 changed files with 30 additions and 15 deletions

View File

@@ -42,10 +42,8 @@ export const Toast: React.FC<ToastProps> = ({ id, message, type, details, onClos
};
const handleMouseLeave = () => {
// Set a timeout to mark as not hovered after 1 second
hoverTimeoutRef.current = setTimeout(() => {
onHoverChange?.(false);
}, 1000);
// Immediately mark as not hovered
onHoverChange?.(false);
};
const handleClose = () => {

View File

@@ -50,6 +50,11 @@ export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ childre
};
const removeToast = (id: string) => {
// Clear any existing timeout for this toast
if (hoverTimeouts.current[id]) {
clearTimeout(hoverTimeouts.current[id]);
delete hoverTimeouts.current[id];
}
dispatch({ type: 'REMOVE_TOAST', payload: id });
};
@@ -59,21 +64,24 @@ export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ childre
// Auto remove toasts after duration, but respect hover state
useEffect(() => {
const timers = toasts.map(toast => {
// Create a copy of current timeouts to track which ones we need to clear
const currentTimeouts = { ...hoverTimeouts.current };
toasts.forEach(toast => {
// Clear any existing timeout for this toast
if (hoverTimeouts.current[toast.id]) {
clearTimeout(hoverTimeouts.current[toast.id]);
delete hoverTimeouts.current[toast.id];
if (currentTimeouts[toast.id]) {
clearTimeout(currentTimeouts[toast.id]);
delete currentTimeouts[toast.id];
}
// If toast is hovered, don't set a timer
if (toast.hovered) {
return null;
return;
}
// If duration is 0, it's persistent
if (toast.duration === 0) {
return null;
return;
}
// Set timeout to remove toast
@@ -81,14 +89,23 @@ export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ childre
removeToast(toast.id);
}, toast.duration);
return { id: toast.id, timeout };
hoverTimeouts.current[toast.id] = timeout;
});
// Cleanup function
// Clear any remaining timeouts for toasts that no longer exist
Object.entries(currentTimeouts).forEach(([id, timeout]) => {
clearTimeout(timeout);
delete hoverTimeouts.current[id];
});
// Cleanup function for when component unmounts or toasts change
return () => {
timers.forEach(timer => {
if (timer) clearTimeout(timer.timeout);
// Clear all active timeouts
Object.values(hoverTimeouts.current).forEach(timeout => {
clearTimeout(timeout);
});
// Reset the timeouts object
hoverTimeouts.current = {};
};
}, [toasts]);