From 62946be82f8f3fd45710d31dd23f91292ed0372f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E6=B6=9B?= Date: Mon, 22 Dec 2025 21:23:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D(ui):=20=E8=A7=A3=E5=86=B3Toa?= =?UTF-8?q?st=E9=80=9A=E7=9F=A5=E4=B8=8D=E6=AD=A3=E5=B8=B8=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复Toast组件中鼠标悬停状态的延迟通知逻辑 - 重构ToastContext中的定时器管理机制,防止内存泄漏 - 优化Toast的hover状态同步,确保定时器正确暂停和恢复 - 增强cleanup机制,清理已删除Toast的残留定时器 - 解决Toast在指定时间后不自动关闭的问题 修复内容: - Toast.tsx: 移除鼠标离开后的1秒延迟,立即通知状态变化 - ToastContext.tsx: 改进定时器生命周期管理和清理逻辑 验证: - 构建成功通过 - 所有测试套件通过(34个测试) - Toast现在能正确按时关闭,悬停暂停功能正常 --- src/components/Toast.tsx | 6 ++--- src/components/ToastContext.tsx | 39 +++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx index 9df3af2..d5e65de 100644 --- a/src/components/Toast.tsx +++ b/src/components/Toast.tsx @@ -42,10 +42,8 @@ export const Toast: React.FC = ({ 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 = () => { diff --git a/src/components/ToastContext.tsx b/src/components/ToastContext.tsx index ed495b9..93c07ae 100644 --- a/src/components/ToastContext.tsx +++ b/src/components/ToastContext.tsx @@ -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,36 +64,48 @@ 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 const timeout = setTimeout(() => { 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]);