全局错误框自动消失

This commit is contained in:
mtvpls
2026-03-22 15:23:39 +08:00
parent 3132d7608f
commit 2e7f43becf

View File

@@ -1,6 +1,6 @@
'use client';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
interface ErrorInfo {
id: string;
@@ -12,6 +12,42 @@ export function GlobalErrorIndicator() {
const [currentError, setCurrentError] = useState<ErrorInfo | null>(null);
const [isVisible, setIsVisible] = useState(false);
const [isReplacing, setIsReplacing] = useState(false);
const [isClosing, setIsClosing] = useState(false);
const currentErrorRef = useRef<ErrorInfo | null>(null);
const closeTimerRef = useRef<number | null>(null);
const exitTimerRef = useRef<number | null>(null);
const clearCloseTimer = useCallback(() => {
if (closeTimerRef.current !== null) {
window.clearTimeout(closeTimerRef.current);
closeTimerRef.current = null;
}
}, []);
const clearExitTimer = useCallback(() => {
if (exitTimerRef.current !== null) {
window.clearTimeout(exitTimerRef.current);
exitTimerRef.current = null;
}
}, []);
const handleClose = useCallback(() => {
clearCloseTimer();
clearExitTimer();
setIsClosing(true);
setIsReplacing(false);
exitTimerRef.current = window.setTimeout(() => {
setIsVisible(false);
setCurrentError(null);
setIsClosing(false);
exitTimerRef.current = null;
}, 300);
}, [clearCloseTimer, clearExitTimer]);
useEffect(() => {
currentErrorRef.current = currentError;
}, [currentError]);
useEffect(() => {
// 监听自定义错误事件
@@ -23,8 +59,13 @@ export function GlobalErrorIndicator() {
timestamp: Date.now(),
};
clearCloseTimer();
clearExitTimer();
setIsClosing(false);
setIsVisible(true);
// 如果已有错误,开始替换动画
if (currentError) {
if (currentErrorRef.current) {
setCurrentError(newError);
setIsReplacing(true);
@@ -36,23 +77,32 @@ export function GlobalErrorIndicator() {
// 第一次显示错误
setCurrentError(newError);
}
setIsVisible(true);
};
// 监听错误事件
window.addEventListener('globalError', handleError as EventListener);
return () => {
clearCloseTimer();
clearExitTimer();
window.removeEventListener('globalError', handleError as EventListener);
};
}, [currentError]);
}, [clearCloseTimer, clearExitTimer]);
const handleClose = () => {
setIsVisible(false);
setCurrentError(null);
setIsReplacing(false);
};
useEffect(() => {
if (!currentError || isClosing) {
return;
}
clearCloseTimer();
closeTimerRef.current = window.setTimeout(() => {
handleClose();
}, 5000);
return () => {
clearCloseTimer();
};
}, [currentError, handleClose, isClosing, clearCloseTimer]);
if (!isVisible || !currentError) {
return null;
@@ -63,6 +113,10 @@ export function GlobalErrorIndicator() {
{/* 错误卡片 */}
<div
className={`bg-red-500 text-white px-4 py-3 rounded-lg shadow-lg flex items-center justify-between min-w-[300px] max-w-[400px] transition-all duration-300 ${
isClosing
? '-translate-y-4 opacity-0'
: 'translate-y-0 opacity-100'
} ${
isReplacing ? 'scale-105 bg-red-400' : 'scale-100 bg-red-500'
} animate-fade-in`}
>