Added guard to prevent accidental loss when navigating to a different website or closing the browser tab.
This commit is contained in:
parent
75f0a9c72e
commit
046869510a
@ -1,4 +1,4 @@
|
||||
import { useState, useCallback, useRef } from "react";
|
||||
import { useState, useCallback, useRef, useEffect } from "react";
|
||||
import Joi from "joi";
|
||||
import { GeneralIdRef } from "../../utils/GeneralIdRef";
|
||||
import {
|
||||
@ -88,11 +88,17 @@ interface UseFormReturn {
|
||||
handleSsoProviderPickerChange: (name: string, value: GeneralIdRef) => void;
|
||||
handleToggleChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
setState: (updates: Partial<FormState>) => void;
|
||||
hasUnsavedChanges: () => boolean;
|
||||
markAsSaved: () => void;
|
||||
setupNavigationGuard: (onBlock?: (location: Location) => void) => () => void;
|
||||
}
|
||||
|
||||
export const useForm = (initialState: FormState): UseFormReturn => {
|
||||
const [state, setStateInternal] = useState<FormState>(initialState);
|
||||
const schemaRef = useRef<joiSchema>({});
|
||||
const initialDataRef = useRef<FormData>(
|
||||
JSON.parse(JSON.stringify(initialState.data)),
|
||||
);
|
||||
|
||||
const setState = useCallback((updates: Partial<FormState>) => {
|
||||
setStateInternal((prev) => ({ ...prev, ...updates }));
|
||||
@ -564,6 +570,42 @@ export const useForm = (initialState: FormState): UseFormReturn => {
|
||||
[state.data, validate, setState],
|
||||
);
|
||||
|
||||
// Unsaved changes detection
|
||||
const hasUnsavedChanges = useCallback((): boolean => {
|
||||
return (
|
||||
JSON.stringify(state.data) !== JSON.stringify(initialDataRef.current)
|
||||
);
|
||||
}, [state.data]);
|
||||
|
||||
const markAsSaved = useCallback((): void => {
|
||||
initialDataRef.current = JSON.parse(JSON.stringify(state.data));
|
||||
}, [state.data]);
|
||||
|
||||
const setupNavigationGuard = useCallback(
|
||||
(onBlock?: (location: Location) => void): (() => void) => {
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
if (hasUnsavedChanges()) {
|
||||
e.preventDefault();
|
||||
e.returnValue = "";
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("beforeunload", handleBeforeUnload);
|
||||
|
||||
// Return cleanup function
|
||||
return () => {
|
||||
window.removeEventListener("beforeunload", handleBeforeUnload);
|
||||
};
|
||||
},
|
||||
[hasUnsavedChanges],
|
||||
);
|
||||
|
||||
// Setup navigation guard on mount/unmount
|
||||
useEffect(() => {
|
||||
const cleanup = setupNavigationGuard();
|
||||
return cleanup;
|
||||
}, [setupNavigationGuard]);
|
||||
|
||||
const api: any = {
|
||||
state,
|
||||
schema: schemaRef.current,
|
||||
@ -588,6 +630,9 @@ export const useForm = (initialState: FormState): UseFormReturn => {
|
||||
handleToggleChange,
|
||||
handleTasksChange,
|
||||
setState,
|
||||
hasUnsavedChanges,
|
||||
markAsSaved,
|
||||
setupNavigationGuard,
|
||||
};
|
||||
Object.defineProperty(api, "schema", {
|
||||
get: () => schemaRef.current,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user