openpanel/packages/react-router-v6/src/use-prompt-workaround.ts
2024-02-05 10:23:04 +01:00

82 lines
2.2 KiB
TypeScript

/**
* `useBlocker` and `usePrompt` is no longer part of react-router-dom for the routers other than `DataRouter`.
*
* The previous workaround (<v6.4) was to use `block` function in `UNSAFE_NavigationContext` which is now removed.
*
* We're using a workaround from the gist https://gist.github.com/MarksCode/64e438c82b0b2a1161e01c88ca0d0355 with some modifications
* Thanks to @MarksCode(https://github.com/MarksCode) for the workaround.
*/
import React from "react";
import { UNSAFE_NavigationContext as NavigationContext } from "react-router-dom";
function useConfirmExit(confirmExit: () => boolean, when = true) {
const { navigator } = React.useContext(NavigationContext);
React.useEffect(() => {
if (!when) {
return;
}
const go = navigator.go;
const push = navigator.push;
navigator.push = (...args: Parameters<typeof push>) => {
const result = confirmExit();
if (result !== false) {
push(...args);
}
};
navigator.go = (...args: Parameters<typeof go>) => {
const result = confirmExit();
if (result !== false) {
go(...args);
}
};
return () => {
navigator.push = push;
navigator.go = go;
};
}, [navigator, confirmExit, when]);
}
export function usePrompt(
message: string,
when = true,
onConfirm?: () => void,
legacy = false,
) {
const warnWhenListener = React.useCallback(
(e: { preventDefault: () => void; returnValue: string }) => {
e.preventDefault();
e.returnValue = message;
return e.returnValue;
},
[message],
);
React.useEffect(() => {
if (when && !legacy) {
window.addEventListener("beforeunload", warnWhenListener);
}
return () => {
window.removeEventListener("beforeunload", warnWhenListener);
};
}, [warnWhenListener, when, legacy]);
const confirmExit = React.useCallback(() => {
const confirm = window.confirm(message);
if (confirm && onConfirm) {
onConfirm();
}
return confirm;
}, [message]);
useConfirmExit(confirmExit, when);
}