全局错误框自动消失
This commit is contained in:
@@ -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`}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user