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:
gpt-engineer-app[bot]
2025-03-17 16:37:34 +00:00
parent acb9ae3d70
commit a53717c502
5 changed files with 74 additions and 48 deletions

View File

@@ -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}
/> />
</> </>
); );

View File

@@ -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}>

View File

@@ -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(() => {

View File

@@ -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]);

View File

@@ -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>