In react-router
v3, I've been using router.setRouteLeaveHook
to check if a form has unsaved changes, and if so return false
to prevent the transition. Then I would display a custom bootstrap modal dialog with 3 buttons: Save Changes, Discard Changes, and Stay Here.
I can't use react-router
v4's Prompt
ponent to do this because it's not possible to customize the buttons shown in a browser confirm dialog. It seems like they got rid of any way to cancel the transition in favor of only allowing you to ask the user to approve the transition in a browser confirm dialog.
I tried looking in the code for Prompt
but it just passes the dialog message to history
, so that doesn't give me any idea how to set up a v3-style route leave hook.
Is it even possible anymore or did the react-router
devs intentionally decide to remove this capability this for some reason?
In react-router
v3, I've been using router.setRouteLeaveHook
to check if a form has unsaved changes, and if so return false
to prevent the transition. Then I would display a custom bootstrap modal dialog with 3 buttons: Save Changes, Discard Changes, and Stay Here.
I can't use react-router
v4's Prompt
ponent to do this because it's not possible to customize the buttons shown in a browser confirm dialog. It seems like they got rid of any way to cancel the transition in favor of only allowing you to ask the user to approve the transition in a browser confirm dialog.
I tried looking in the code for Prompt
but it just passes the dialog message to history
, so that doesn't give me any idea how to set up a v3-style route leave hook.
Is it even possible anymore or did the react-router
devs intentionally decide to remove this capability this for some reason?
3 Answers
Reset to default 3According to the history package docs, you can replace window.confirm
with anything you like:
By default, window.confirm is used to show prompt messages to the user. If you need to override this behavior (or if you're using createMemoryHistory, which doesn't assume a DOM environment), provide a getUserConfirmation function when you create your history object.
So if you want to use your own dialog, something like this should see you through:
const history = createHistory({
getUserConfirmation(message, callback) {
showMyCustomDialog(message)
.then(result => callback(result === 'The YES button'))
}
})
This means that whatever getUserConfirmation
message you set is set for the entire session, but you could abstract it out to your navigation blocking layer that holds additional details for your dialog, e.g. title, button text, colours etc.
Alternatively, you could hijack the message
argument and use it for dialog configuration, though that may smell a bit nasty. But it's not a perfect system so anything you do will probably be a bit of a hack.
React Router v4 allows you to pass this method through when you create your router (see here):
<BrowserRouter getUserConfirmation={yourConfirmationFunction} />
Can use Prompt
to show custom dialogue. Credit and detailed explanation here.
Prompt
requires a message prop, here we can use a custom function for a dialogue and it should return false
to prevent navigation.
const BlockingPage = () => {
const [block, setBlock] = useState(true);
const blockedNavigation = (nLocation) => {
//nLocation gives the next location object
/**
* Your custom logic
*
**/
//required to block navigation
return false
}
return(
<div>
<Prompt when={block} message={blockedNavigation}/>
</div>
)
}
I don't think this is possible. react-router-v4 uses a package called history that in turns uses the HTML5 history api. The history api only notifies you when you hit the back button (onpopstate), If you think about it this makes lots of sense, since you would not want to give a website the power of not letting you move between pages.
The best you can do is the window onbeforeunload event, that creates a prompt for you asking confirmation from the user, but this is exactly what react-router exposes for you to use.
You might get some of the functionality you want by monkey-patching react-router's internal history object, and that way you can add your own behaviour. But there is a caveat, this is only going to work when you react-router's <Link />
ponent and friends, so you will not be able to intercept refreshes and other things you may want.
If you want to go that route, let me know I can give you some insight or code examples about how might that work and I will update my answer.