Fix transaction deletion issue
Addresses the issue where deleting transactions caused the homepage and expense page to freeze.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import { Transaction } from './TransactionCard';
|
||||
import TransactionEditDialog from './TransactionEditDialog';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
@@ -22,6 +22,15 @@ const RecentTransactionsSection: React.FC<RecentTransactionsSectionProps> = ({
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const { updateTransaction, deleteTransaction } = useBudget();
|
||||
|
||||
// 삭제 중인 ID 추적
|
||||
const deletingIdRef = useRef<string | null>(null);
|
||||
|
||||
// 타임아웃 추적
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// 삭제 요청 타임스탬프 추적 (급발진 방지)
|
||||
const lastDeleteTimeRef = useRef<Record<string, number>>({});
|
||||
|
||||
const handleTransactionClick = (transaction: Transaction) => {
|
||||
setSelectedTransaction(transaction);
|
||||
@@ -40,42 +49,56 @@ const RecentTransactionsSection: React.FC<RecentTransactionsSectionProps> = ({
|
||||
const handleDeleteTransaction = useCallback(async (id: string): Promise<boolean> => {
|
||||
try {
|
||||
// 이미 삭제 중인 경우 중복 요청 방지
|
||||
if (isDeleting) {
|
||||
if (isDeleting || deletingIdRef.current === id) {
|
||||
console.warn('이미 삭제 작업이 진행 중입니다');
|
||||
return true;
|
||||
}
|
||||
|
||||
// 급발진 방지 (300ms 내 동일 ID 연속 호출 차단)
|
||||
const now = Date.now();
|
||||
if (lastDeleteTimeRef.current[id] && (now - lastDeleteTimeRef.current[id] < 300)) {
|
||||
console.warn('삭제 요청이 너무 빠릅니다. 무시합니다.');
|
||||
return true;
|
||||
}
|
||||
|
||||
// 타임스탬프 업데이트
|
||||
lastDeleteTimeRef.current[id] = now;
|
||||
|
||||
// 삭제 상태 설정
|
||||
setIsDeleting(true);
|
||||
deletingIdRef.current = id;
|
||||
|
||||
// 먼저 다이얼로그 닫기 (UI 응답성 확보)
|
||||
setIsDialogOpen(false);
|
||||
|
||||
// 3초 타임아웃 설정 (UI 멈춤 방지)
|
||||
const timeoutPromise = new Promise<boolean>(resolve => {
|
||||
const timeout = setTimeout(() => {
|
||||
console.warn('삭제 타임아웃 - 강제 완료');
|
||||
setIsDeleting(false);
|
||||
resolve(true);
|
||||
}, 3000);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
});
|
||||
// 타임아웃 생성 (1.5초 제한)
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
// 실제 삭제 요청
|
||||
const deletePromise = (async () => {
|
||||
try {
|
||||
deleteTransaction(id);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('삭제 요청 실패:', error);
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
// 안전 장치: 1.5초 후 상태 강제 초기화
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
console.warn('삭제 타임아웃 - 강제 상태 초기화');
|
||||
setIsDeleting(false);
|
||||
deletingIdRef.current = null;
|
||||
}, 1500);
|
||||
|
||||
// 둘 중 먼저 완료되는 것으로 처리
|
||||
await Promise.race([deletePromise, timeoutPromise]);
|
||||
// 삭제 요청 실행 및 즉시 리턴
|
||||
try {
|
||||
// 컨텍스트를 통한 삭제 요청
|
||||
deleteTransaction(id);
|
||||
console.log('삭제 요청 전송 완료');
|
||||
} catch (error) {
|
||||
console.error('삭제 요청 실패:', error);
|
||||
}
|
||||
|
||||
// 즉시 정상 반환 (트랜잭션은 비동기식으로 처리)
|
||||
// 0.5초 후 상태 초기화
|
||||
setTimeout(() => {
|
||||
setIsDeleting(false);
|
||||
deletingIdRef.current = null;
|
||||
}, 500);
|
||||
|
||||
setIsDeleting(false);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('트랜잭션 삭제 중 오류:', error);
|
||||
@@ -83,6 +106,12 @@ const RecentTransactionsSection: React.FC<RecentTransactionsSectionProps> = ({
|
||||
// 항상 상태 정리
|
||||
setIsDeleting(false);
|
||||
setIsDialogOpen(false);
|
||||
deletingIdRef.current = null;
|
||||
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: "삭제 실패",
|
||||
@@ -94,6 +123,15 @@ const RecentTransactionsSection: React.FC<RecentTransactionsSectionProps> = ({
|
||||
return false;
|
||||
}
|
||||
}, [deleteTransaction, isDeleting]);
|
||||
|
||||
// 컴포넌트 언마운트 시 타임아웃 정리
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return amount.toLocaleString('ko-KR') + '원';
|
||||
@@ -147,3 +185,4 @@ const RecentTransactionsSection: React.FC<RecentTransactionsSectionProps> = ({
|
||||
};
|
||||
|
||||
export default RecentTransactionsSection;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user