Fix issue deleting transactions
Fixes an issue where deleting a transaction on the transaction history screen would cause the application to freeze.
This commit is contained in:
@@ -23,16 +23,9 @@ interface TransactionCardProps {
|
|||||||
|
|
||||||
const TransactionCard: React.FC<TransactionCardProps> = ({
|
const TransactionCard: React.FC<TransactionCardProps> = ({
|
||||||
transaction,
|
transaction,
|
||||||
onUpdate
|
|
||||||
}) => {
|
}) => {
|
||||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
||||||
const { title, amount, date, category, type } = transaction;
|
const { title, amount, date, category } = transaction;
|
||||||
|
|
||||||
const handleSaveTransaction = (updatedTransaction: Transaction) => {
|
|
||||||
if (onUpdate) {
|
|
||||||
onUpdate(updatedTransaction);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -54,7 +47,6 @@ const TransactionCard: React.FC<TransactionCardProps> = ({
|
|||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
open={isEditDialogOpen}
|
open={isEditDialogOpen}
|
||||||
onOpenChange={setIsEditDialogOpen}
|
onOpenChange={setIsEditDialogOpen}
|
||||||
onSave={handleSaveTransaction}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogClose
|
DialogClose,
|
||||||
|
DialogDescription
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Form } from '@/components/ui/form';
|
import { Form } from '@/components/ui/form';
|
||||||
@@ -78,20 +79,19 @@ const TransactionEditDialog: React.FC<TransactionEditDialogProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
// 컨텍스트를 통해 트랜잭션 삭제
|
// 다이얼로그 닫기를 먼저 수행 (UI 블로킹 방지)
|
||||||
deleteTransaction(transaction.id);
|
|
||||||
|
|
||||||
// 부모 컴포넌트의 onDelete 콜백이 있다면 호출
|
|
||||||
if (onDelete) {
|
|
||||||
onDelete(transaction.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
|
|
||||||
toast({
|
// 약간의 지연 후 삭제 작업 수행 (안정성 향상)
|
||||||
title: "지출이 삭제되었습니다",
|
setTimeout(() => {
|
||||||
description: `${transaction.title} 항목이 삭제되었습니다.`,
|
// 컨텍스트를 통해 트랜잭션 삭제
|
||||||
});
|
deleteTransaction(transaction.id);
|
||||||
|
|
||||||
|
// 부모 컴포넌트의 onDelete 콜백이 있다면 호출
|
||||||
|
if (onDelete) {
|
||||||
|
onDelete(transaction.id);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -99,6 +99,9 @@ const TransactionEditDialog: React.FC<TransactionEditDialogProps> = ({
|
|||||||
<DialogContent className={`sm:max-w-md mx-auto ${isMobile ? 'rounded-xl overflow-hidden' : ''}`}>
|
<DialogContent className={`sm:max-w-md mx-auto ${isMobile ? 'rounded-xl overflow-hidden' : ''}`}>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>지출 수정</DialogTitle>
|
<DialogTitle>지출 수정</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
지출 내역을 수정하거나 삭제할 수 있습니다.
|
||||||
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { Transaction } from '../types';
|
import { Transaction } from '../types';
|
||||||
import {
|
import {
|
||||||
@@ -11,6 +12,7 @@ import { toast } from '@/hooks/useToast.wrapper'; // 래퍼 사용
|
|||||||
export const useTransactionState = () => {
|
export const useTransactionState = () => {
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
const [lastDeletedId, setLastDeletedId] = useState<string | null>(null);
|
const [lastDeletedId, setLastDeletedId] = useState<string | null>(null);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
// 초기 트랜잭션 로드 및 이벤트 리스너 설정
|
// 초기 트랜잭션 로드 및 이벤트 리스너 설정
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -72,9 +74,15 @@ export const useTransactionState = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 트랜잭션 삭제 함수
|
// 트랜잭션 삭제 함수 - 안정성 개선
|
||||||
const deleteTransaction = useCallback((transactionId: string) => {
|
const deleteTransaction = useCallback((transactionId: string) => {
|
||||||
console.log('트랜잭션 삭제:', transactionId);
|
// 이미 삭제 중이면 중복 삭제 방지
|
||||||
|
if (isDeleting) {
|
||||||
|
console.log('이미 삭제 작업이 진행 중입니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('트랜잭션 삭제 시작:', transactionId);
|
||||||
|
|
||||||
// 중복 삭제 방지
|
// 중복 삭제 방지
|
||||||
if (lastDeletedId === transactionId) {
|
if (lastDeletedId === transactionId) {
|
||||||
@@ -82,24 +90,50 @@ export const useTransactionState = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsDeleting(true);
|
||||||
setLastDeletedId(transactionId);
|
setLastDeletedId(transactionId);
|
||||||
|
|
||||||
setTransactions(prev => {
|
try {
|
||||||
const updated = prev.filter(transaction => transaction.id !== transactionId);
|
setTransactions(prev => {
|
||||||
saveTransactionsToStorage(updated);
|
// 기존 트랜잭션 목록 백업 (문제 발생 시 복원용)
|
||||||
|
const originalTransactions = [...prev];
|
||||||
// 토스트는 한 번만 호출
|
|
||||||
toast({
|
// 삭제할 항목 필터링
|
||||||
title: "지출이 삭제되었습니다",
|
const updated = prev.filter(transaction => transaction.id !== transactionId);
|
||||||
description: "지출 항목이 성공적으로 삭제되었습니다.",
|
|
||||||
|
// 항목이 실제로 삭제되었는지 확인
|
||||||
|
if (updated.length === originalTransactions.length) {
|
||||||
|
console.log('삭제할 트랜잭션을 찾을 수 없음:', transactionId);
|
||||||
|
setIsDeleting(false);
|
||||||
|
return originalTransactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 저장소에 업데이트된 목록 저장
|
||||||
|
saveTransactionsToStorage(updated);
|
||||||
|
|
||||||
|
// 토스트 메시지 표시
|
||||||
|
toast({
|
||||||
|
title: "지출이 삭제되었습니다",
|
||||||
|
description: "지출 항목이 성공적으로 삭제되었습니다.",
|
||||||
|
});
|
||||||
|
|
||||||
|
return updated;
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
return updated;
|
console.error('트랜잭션 삭제 중 오류 발생:', error);
|
||||||
});
|
toast({
|
||||||
|
title: "삭제 실패",
|
||||||
// 5초 후 lastDeletedId 초기화
|
description: "지출 항목 삭제 중 오류가 발생했습니다.",
|
||||||
setTimeout(() => setLastDeletedId(null), 5000);
|
variant: "destructive"
|
||||||
}, [lastDeletedId]);
|
});
|
||||||
|
} finally {
|
||||||
|
// 삭제 상태 초기화 (1초 후)
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsDeleting(false);
|
||||||
|
setLastDeletedId(null);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}, [lastDeletedId, isDeleting]);
|
||||||
|
|
||||||
// 트랜잭션 초기화 함수
|
// 트랜잭션 초기화 함수
|
||||||
const resetTransactions = useCallback(() => {
|
const resetTransactions = useCallback(() => {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export const useTransactionsCore = () => {
|
|||||||
|
|
||||||
// 데이터 강제 새로고침
|
// 데이터 강제 새로고침
|
||||||
const refreshTransactions = useCallback(() => {
|
const refreshTransactions = useCallback(() => {
|
||||||
|
console.log('트랜잭션 강제 새로고침');
|
||||||
setRefreshKey(prev => prev + 1);
|
setRefreshKey(prev => prev + 1);
|
||||||
loadTransactions();
|
loadTransactions();
|
||||||
}, [loadTransactions, setRefreshKey]);
|
}, [loadTransactions, setRefreshKey]);
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ const Transactions = () => {
|
|||||||
setSearchQuery,
|
setSearchQuery,
|
||||||
handlePrevMonth,
|
handlePrevMonth,
|
||||||
handleNextMonth,
|
handleNextMonth,
|
||||||
updateTransaction,
|
refreshTransactions,
|
||||||
deleteTransaction,
|
|
||||||
totalExpenses,
|
totalExpenses,
|
||||||
} = useTransactions();
|
} = useTransactions();
|
||||||
|
|
||||||
@@ -48,15 +47,13 @@ const Transactions = () => {
|
|||||||
const handleVisibilityChange = () => {
|
const handleVisibilityChange = () => {
|
||||||
if (document.visibilityState === 'visible') {
|
if (document.visibilityState === 'visible') {
|
||||||
console.log('거래내역 페이지 보임 - 데이터 새로고침');
|
console.log('거래내역 페이지 보임 - 데이터 새로고침');
|
||||||
// 상태 업데이트 트리거
|
refreshTransactions();
|
||||||
setIsDataLoaded(prev => !prev);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
console.log('거래내역 페이지 포커스 - 데이터 새로고침');
|
console.log('거래내역 페이지 포커스 - 데이터 새로고침');
|
||||||
// 상태 업데이트 트리거
|
refreshTransactions();
|
||||||
setIsDataLoaded(prev => !prev);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||||
@@ -66,7 +63,7 @@ const Transactions = () => {
|
|||||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||||
window.removeEventListener('focus', handleFocus);
|
window.removeEventListener('focus', handleFocus);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [refreshTransactions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-neuro-background pb-24">
|
<div className="min-h-screen bg-neuro-background pb-24">
|
||||||
@@ -168,8 +165,7 @@ const Transactions = () => {
|
|||||||
{transactions.map(transaction => (
|
{transactions.map(transaction => (
|
||||||
<TransactionCard
|
<TransactionCard
|
||||||
key={transaction.id}
|
key={transaction.id}
|
||||||
transaction={transaction}
|
transaction={transaction}
|
||||||
onUpdate={updateTransaction}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user