unsaveChangeWarning hooks

This commit is contained in:
hamid zarghami
2025-10-13 10:48:12 +03:30
parent 5b376b83d1
commit d3d63a922c
3 changed files with 85 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
import { useEffect, useRef } from "react";
import { useLocation } from "react-router-dom";
/**
* هوک برای هشدار به کاربر هنگام خروج از صفحه با تغییرات ذخیره نشده
* این هوک از رفرش، بستن تب، و navigation داخلی (دکمه برگشت، لینک‌ها) جلوگیری می‌کند
* @param isDirty - آیا فرم تغییر کرده است؟
* @param message - پیام هشدار (اختیاری)
*/
export const useUnsavedChangesWarning = (
isDirty: boolean,
message: string = "تغییرات شما ذخیره نشده‌اند. آیا مطمئن هستید که می‌خواهید این صفحه را ترک کنید؟"
) => {
const location = useLocation();
const currentPath = useRef(location.pathname);
const isNavigatingRef = useRef(false);
// برای جلوگیری از بستن/رفرش مرورگر
useEffect(() => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
if (isDirty) {
e.preventDefault();
e.returnValue = "";
return "";
}
};
if (isDirty) {
window.addEventListener("beforeunload", handleBeforeUnload);
}
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
};
}, [isDirty]);
// برای جلوگیری از navigation داخلی (دکمه برگشت مرورگر)
useEffect(() => {
if (!isDirty) {
currentPath.current = location.pathname;
return;
}
const handlePopState = () => {
if (isDirty && !isNavigatingRef.current) {
const confirmLeave = window.confirm(message);
if (!confirmLeave) {
// اگر کاربر لغو کرد، به صفحه قبلی برگرد
window.history.pushState(null, "", currentPath.current);
} else {
// اگر کاربر تایید کرد، اجازه navigation بده
currentPath.current = location.pathname;
}
}
};
// یک state جدید به history اضافه کن تا بتونیم popstate رو catch کنیم
window.history.pushState(null, "", location.pathname);
window.addEventListener("popstate", handlePopState);
return () => {
window.removeEventListener("popstate", handlePopState);
};
}, [isDirty, message, location.pathname]);
// Update current path when location changes
useEffect(() => {
if (!isDirty) {
currentPath.current = location.pathname;
}
}, [location.pathname, isDirty]);
};