I'm able to capture push event and control the navigation if my page has unsaved changes (isDirty: true
) in react-router-dom
v6 whereas I'm not able to wrap my head for pop event. Seems navigator
from NavigationContext
doesn't have pop event exposed directly to use. Can anyone help?
My Code for push event looks like this -
import { useEffect } from 'react';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';
const useConfirmExit = function (confirmExit: () => Promise<boolean>, when = true, callbackfn: (arg0: any) => void): void {
const {navigator} = useContext(NavigationContext);
useEffect(() => {
if (!when) {
return () => { };
}
const {go, push} = navigator;
navigator.push = async (...args: Parameters<typeof push>) => {
const result = await confirmExit();
if (result !== false) {
callbackfn(arg0);
push(...args);
}
};
navigator.go = async (delta: number) => {
const result = await confirmExit();
if (result !== false) {
go(delta);
}
};
return () => {
navigator.push = push;
navigator.go = go;
};
}, [navigator, confirmExit, when]);
};
confirmExit
is just another async function which is taking input of user if they want to navigate away from the unsaved page.
The navigator
doesn't have any of the navigation actions, e.g. PUSH, POP, REPLACE, but it does have the go
function to move forward/backward through the history stack. navigator.go(-1)
is synonymous to a goBack
or back navigation.
/** * A Navigator is a "location changer"; it's how you get to different locations. * * Every history instance conforms to the Navigator interface, but the * distinction is useful primarily when it comes to the low-level <Router> API * where both the location and a navigator must be provided separately in order * to avoid "tearing" that may occur in a suspense-enabled app if the action * and/or location were to be read directly from the history instance. */ export interface Navigator { createHref: History["createHref"]; // Optional for backwards-compat with Router/HistoryRouter usage (edge case) encodeLocation?: History["encodeLocation"]; go: History["go"]; push(to: To, state?: any, opts?: NavigateOptions): void; replace(to: To, state?: any, opts?: NavigateOptions): void; }
You could augment the go
function as well, something like the following:
import { useEffect } from 'react';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';
function useConfirmExit(confirmExit: () => Promise<boolean>, when = true): void {
const { navigator } = useContext(NavigationContext);
useEffect(() => {
if (!when) {
return () => { };
}
const { go, push } = navigator;
navigator.go = async (delta: number) => {
const result = await confirmExit();
if (result !== false) {
console.log('Save to be invoked');
go(delta);
}
};
navigator.push = async (...args: Parameters<typeof push>) => {
const result = await confirmExit();
if (result !== false) {
console.log('Save to be invoked');
push(...args);
}
};
return () => {
navigator.go = go;
navigator.push = push;
};
}, [navigator, confirmExit, when]);
}