An alternative solution is to implement a custom router like suggested here: react router v6 navigate outside of components
The implementation of the Router is pretty simple, and once you have it setup you can put your history object in a React Context or your state management solution and call history.block
when you want to trigger the prompt.
Something like this:
const { history } = useNavigationContext();
// This ref is used to save the destination location to which we plan to navigate after the dirty check is complete
const navigateDestination = useRef<Transition>();
const onConfirmLeave = () => {
navigateDestination.current?.retry();
};
const unblockNavigationRef = useRef<Function>();
useEffect(() => {
if (isDirty) {
// Remove previous block function if exists.
unblockNavigationRef.current?.();
// Block route navigation requests when the isDirty flag is on.
// Instead of navigating when an attempt to navigate happens, save the destination route and render the dirty prompt
unblockNavigationRef.current = history.block((transition: Transition) => {
navigateDestination.current = transition;
renderPrompt(onConfirmLeave);
});
} else {
// Unblock navigation when the dirty state is cleared
unblockNavigationRef.current?.();
}
}, [isDirty, history]);