Refactor TransactionDeleteAlert component
Refactor TransactionDeleteAlert component to improve maintainability.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Trash2, Loader2 } from 'lucide-react';
|
import { Trash2, Loader2 } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
@@ -13,75 +13,22 @@ import {
|
|||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger
|
AlertDialogTrigger
|
||||||
} from '@/components/ui/alert-dialog';
|
} from '@/components/ui/alert-dialog';
|
||||||
|
import { useDeleteAlert } from '@/hooks/transactions/useDeleteAlert';
|
||||||
|
|
||||||
interface TransactionDeleteAlertProps {
|
interface TransactionDeleteAlertProps {
|
||||||
onDelete: () => Promise<boolean> | boolean;
|
onDelete: () => Promise<boolean> | boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 트랜잭션 삭제 확인 다이얼로그 - 완전히 개선된 버전
|
* 트랜잭션 삭제 확인 다이얼로그 - 리팩토링된 버전
|
||||||
* 삭제 중복 방지, 상태 관리 개선, 메모리 누수 방지 로직 추가
|
* 삭제 로직을 useDeleteAlert 훅으로 분리하여 컴포넌트 간소화
|
||||||
*/
|
*/
|
||||||
const TransactionDeleteAlert: React.FC<TransactionDeleteAlertProps> = ({ onDelete }) => {
|
const TransactionDeleteAlert: React.FC<TransactionDeleteAlertProps> = ({ onDelete }) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
// 삭제 관련 로직을 커스텀 훅으로 분리
|
||||||
const [isDeleting, setIsDeleting] = useState(false);
|
const { isOpen, isDeleting, handleDelete, handleOpenChange } = useDeleteAlert(onDelete);
|
||||||
|
|
||||||
// 타임아웃 참조 저장 (메모리 누수 방지용)
|
|
||||||
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
||||||
|
|
||||||
// 클린업 함수 - 메모리 누수 방지
|
|
||||||
const clearTimeouts = () => {
|
|
||||||
if (timeoutRef.current) {
|
|
||||||
clearTimeout(timeoutRef.current);
|
|
||||||
timeoutRef.current = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 컴포넌트 언마운트 시 모든 타임아웃 제거
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
clearTimeouts();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDelete = async () => {
|
|
||||||
// 이미 삭제 중이면 중복 실행 방지
|
|
||||||
if (isDeleting) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 삭제 상태 활성화
|
|
||||||
setIsDeleting(true);
|
|
||||||
|
|
||||||
// 다이얼로그 즉시 닫기 (UI 응답성 개선)
|
|
||||||
setIsOpen(false);
|
|
||||||
|
|
||||||
// UI 애니메이션 완료 후 삭제 실행
|
|
||||||
timeoutRef.current = setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
// 삭제 함수 실행
|
|
||||||
await onDelete();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('삭제 처리 오류:', error);
|
|
||||||
} finally {
|
|
||||||
// 모든 작업 완료 후 상태 초기화 (약간 지연)
|
|
||||||
timeoutRef.current = setTimeout(() => {
|
|
||||||
setIsDeleting(false);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}, 150);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('삭제 핸들러 오류:', error);
|
|
||||||
setIsDeleting(false);
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AlertDialog open={isOpen} onOpenChange={(open) => {
|
<AlertDialog open={isOpen} onOpenChange={handleOpenChange}>
|
||||||
// 삭제 중에는 상태 변경 방지
|
|
||||||
if (isDeleting && !open) return;
|
|
||||||
setIsOpen(open);
|
|
||||||
}}>
|
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
74
src/hooks/transactions/useDeleteAlert.ts
Normal file
74
src/hooks/transactions/useDeleteAlert.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
|
||||||
|
import { useState, useRef, useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 트랜잭션 삭제 알림 관련 로직을 담당하는 커스텀 훅
|
||||||
|
*/
|
||||||
|
export const useDeleteAlert = (onDelete: () => Promise<boolean> | boolean) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
|
// 타임아웃 참조 저장 (메모리 누수 방지용)
|
||||||
|
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
|
// 클린업 함수 - 메모리 누수 방지
|
||||||
|
const clearTimeouts = () => {
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current);
|
||||||
|
timeoutRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 컴포넌트 언마운트 시 모든 타임아웃 제거
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
clearTimeouts();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
// 이미 삭제 중이면 중복 실행 방지
|
||||||
|
if (isDeleting) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 삭제 상태 활성화
|
||||||
|
setIsDeleting(true);
|
||||||
|
|
||||||
|
// 다이얼로그 즉시 닫기 (UI 응답성 개선)
|
||||||
|
setIsOpen(false);
|
||||||
|
|
||||||
|
// UI 애니메이션 완료 후 삭제 실행
|
||||||
|
timeoutRef.current = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
// 삭제 함수 실행
|
||||||
|
await onDelete();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('삭제 처리 오류:', error);
|
||||||
|
} finally {
|
||||||
|
// 모든 작업 완료 후 상태 초기화 (약간 지연)
|
||||||
|
timeoutRef.current = setTimeout(() => {
|
||||||
|
setIsDeleting(false);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('삭제 핸들러 오류:', error);
|
||||||
|
setIsDeleting(false);
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 다이얼로그 상태 관리
|
||||||
|
const handleOpenChange = (open: boolean) => {
|
||||||
|
// 삭제 중에는 상태 변경 방지
|
||||||
|
if (isDeleting && !open) return;
|
||||||
|
setIsOpen(open);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isOpen,
|
||||||
|
isDeleting,
|
||||||
|
handleDelete,
|
||||||
|
handleOpenChange
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user