React-Router v4 - Prevent Transition With Function - reactjs

I was able to prevent navigation as per the v4 docs, but I'm trying to hook up a function so that I can use a modal instead of an alert.
Function:
abandonForm = (route) => {
this.props.showModal('confirm');
console.log('leaving..');
}
In my page:
<NavigationPrompt when={true} message={(location) => this.abandonForm('confirm')} />
this.props.showModal('confirm') activates the modal successfully, but behind the modal the page still transitions - how can I prevent transition until a button in the modal is clicked?

Browsers only allow navigation cancellation by means of the alert box that you've mentioned. This restriction is motivated by phishing/scamming sites that try to use javascript gimmicks to create user experiences that convincingly mimic something that a browser or the OS would do (whom the user trusts). Even the format of the text shown in the alert box is crafted so that it's obvious that it originates from the site.
Of course, as long as the current URL stays within your app, you have control over it using react-router's history. For example you can do the following on navigation:
allow the navigation without confirmation
immediately navigate back to the previous location, but now with a modal on top
navigate away for real this time when the user clicks on a button in the modal.
The disadvantage of this approach (leaving out the sheer complexity of it) is that the user will not get a confirmation dialog if they try to navigate to a different site entirely.

Use:
this.unBlock = this.props.history.block((location, navigateToSelectedRoute) => {
// save navigateToSelectedRoute eg this.navigateToSelectedRoute =
// navigateToSelectedRoute;
// use this.navigateToSelectedRoute() afterwards to navigate to link
// show custom modal using setState
});
and when unblocking is done then call this.unBlock() to remove the listener.
Documentation here for history api

Related

Confirmation on browser back button

I'm trying to achieve the following with Gatsby
The user is on a form page, if they hit the browser back button, a pop up would appear, asking if they want to leave.
If the user selects ok, then it would go back.
If the user selects cancel, then it would stay on this page
I was able to "almost" make it happen by doing the following
useEffect(() => {
const confirmExit = e => {
const leaveThisPage = window.confirm("Would you like to leave this page?")
if (!leaveThisPage) {
window.history.forward()
}
}
window.addEventListener("popstate", confirmExit)
return () => {
window.removeEventListener("popstate", confirmExit)
}
}, [])
There is one issue, if the user selects cancel, then the browser would go to the previous page. Then window.history.forward() would fire and sends them back.
I noticed that popstate event cannot be cancelled, so e.preventDefault() won't work.
Note: I also tried to test with window.onbeforeunload, but it only triggers if I close to window, or if my previous is from outside my app. Is there a work around for my issue?
Gatsby is using #reach/router under the hood, which doesn't support intercepting and preventing navigation (emphasis mine):
No history blocking. I found that the only use-case I had was preventing the user from navigating away from a half-filled out form. Not only is it pretty easy to just save the form state to session storage and bring it back when they return, but history blocking doesn’t happen when you navigate away from the app (say to another domain). This kept me from actually using history blocking and always opting to save the form state to session storage. (Source)
I second the recommendation for saving the form state using localStorage.

React route url and view changes but component function still active. Why?

I have an event function that is triggered by a button click. It's purpose is to keep the body position fixed keeping it from scrolling until you click the button again to close/toggle it off.
It works fine but, when I click to another page without toggling it off it is still active. Meaning the new page will not scroll because the body position is fixed.
I am new to React FYI
My code:
bodyFixed(event) {
document.body.classList.add('body-fixed');
}
bodyRelative(e) {
document.body.classList.remove('body-fixed');
}
I am using react-static withRouteData, RouteData, Router and I have no issues on those pages. But, on pages like an article page where the route doesn't change the same way. This is where I am seeing the problem.
Is there something I can wrap it with so that when I click to a new page it goes back to default?
Please first ask if you need more information and I will gladly add more.
Yes, you can call bodyRelative method in componentWillUnmount lifecycle hook to unset the class. Something along those lines:
componentWillUnmount() {
this.bodyRelative()
}

Modal behavior back/forward button using react-router 4

I have a React app using React Router 4. A login modal is used to authenticate users. In order to support the UX requirements I've been given, I need to have the back button close the modal.
I was able to accomplish a login modal that supports this using a simple push state:
const {history,location} = this.props
const nextState = {showLogin: true}
history.push({...location, state: {...location.state, ...nextState}})
The behavior works as expected. HOWEVER… it would be ideal if pressing the forward button did not reopen the modal.
I don't see a way to pop a location off history with react router. But what I'd like to do is listen to route changes and if the location.state changes back to the showLogin modal being falsey, but the location is the same, it pops the previous location off history.
Use history.replace('') instead of history.push('') while going back.This way, the existing login route, will get replaced from the history stack

Alert user if the current page is temporarily left with React + React-Router

If a user attempts to change the active page (window/tab/etc) from our page to another while myState is true, we want to notify/alert the user in React JS using react-router.
I tried implementing it with the help TransitionHook and React-router's Confirming Navigation article. Though these only point out / work when user wants to close the page or reload it. Whereas I need to know when user just temporarily leaves the page without necessarily closing it.
How can I achieve this?
If by "leaving the page", you mean that the page is open, but a different window has popped up, you could consider using the document.hasFocus property. Here is one way I handled a problem that was tangentially related:
componentDidMount: function (){
setInterval(()=>{
if (document.hasFocus()){
this.checkServerState();
}
},
},

Displaying custom dialog modal for route change confirmation ( using history.listenBeforeUnload )

When refreshing the page or navigating away from the site (aka losing temporary storage), I need to show the user a confirmation dialog box telling him that he will lose all unsaved data.
He can either choose to continue navigating or to cancel the transition, as expected.
I am handling this using the below code:
componentWillMount() {
const history = useBeforeUnload(createHistory)();
history.listenBeforeUnload(() => {
confirm('You will lose all unsaved information, proceed?');
});
}
This works fine, however, as per default, ReactJS uses window.confirm and others ( see: https://github.com/reactjs/history/issues/14 ) to display a confirmation dialog box.
What I have to do is to replace this box with a custom modal for confirmation.
Or at least to change its styling; heavily.
Now, I know some amount of customisation is possible through using getUserConfirmation ( https://github.com/reactjs/history/blob/master/docs/ConfirmingNavigation.md )
I'm unsure how to use the getUserConfirmation to customise my message dialog box, if this is possible, or how this should be done.
I've also seen alternatives like http://bootboxjs.com/, unsure if this is a proper way to go.
Does anyone know how should I go about this & how much customisation is possible?

Resources