import type { History, Blocker, Transition } from 'history';
import { useEffect } from 'react';
// see: https://gist.github.com/rmorse/426ffcc579922a82749934826fa9f743
import { Navigator as BaseNavigator } from 'react-router-dom';
interface Navigator extends BaseNavigator {
block?: History['block'];
}
export interface FormBasedPreventNavigationProps {
promptMessage?: string;
preventNavigate: boolean;
navigator: Navigator;
}
/** Only use this if the project uses `` or any router that does not support the new
* [react-router Data API](https://reactrouter.com/en/main/routers/picking-a-router#using-v64-data-apis)
*
* Legacy function to prevent the user from navigating away from a form if there are any changes.
*/
export default function LegacyFormBasedPreventNavigation({
promptMessage = 'Changes you made may not be saved.',
preventNavigate,
navigator,
}: FormBasedPreventNavigationProps) {
useEffect(() => {
if (!preventNavigate) {
return;
}
let unblock = () => {};
const push = navigator.push;
// TODO: https://reactrouter.com/docs/en/v6/upgrading/v5#prompt-is-not-currently-supported
// this is a workaround until we get native support for prompt on navigate
if (navigator.block) {
const blocker: Blocker = (tx) => {
if (window.confirm(promptMessage)) {
tx.retry();
}
};
unblock = navigator.block((tx: Transition) => {
const autoUnblockingTx = {
...tx,
retry() {
// Automatically unblock the transition so it can play all the way
// through before retrying it. TODO: Figure out how to re-enable
// this block if the transition is cancelled for some reason.
unblock();
tx.retry();
},
};
blocker(autoUnblockingTx);
});
} else {
//https://gist.github.com/MarksCode/64e438c82b0b2a1161e01c88ca0d0355
navigator.push = (...args: Parameters) => {
if (window.confirm(promptMessage)) {
push(...args);
}
};
}
window.addEventListener('beforeunload', beforeUnload);
return () => {
unblock();
navigator.push = push;
window.removeEventListener('beforeunload', beforeUnload);
};
function beforeUnload(e: BeforeUnloadEvent) {
e.preventDefault();
e.returnValue = promptMessage;
}
}, [preventNavigate, promptMessage, navigator]);
return null;
}