|
/* |
|
In standard web apps, when users navigate away to a different page or website or attempt to refresh a page, |
|
we can use the window event listener `beforeunload` to help users avoid losing the work. |
|
|
|
However, when a user clicks on a native NextJS <Link> component, the navigation is not intercepted |
|
because behind the scenes, no page changes are actually happening. |
|
To intercept user navigation in a NextJS app, we can subscribe to the |
|
router routeChangeError event and throw an error. |
|
Note: we must throw an Error string - not a `new Error()` - which is why in the code below, we have |
|
// eslint-disable-next-line no-throw-literal |
|
above the line throwing the error. |
|
*/ |
|
|
|
import Router from "next/router"; |
|
import { useEffect } from 'react'; |
|
|
|
const YourComponent = () => { |
|
|
|
// Relevent code begin |
|
const shouldBlockNavigation = true; // set this up however you want |
|
|
|
useEffect(() => { |
|
const nativeBrowserHandler = (event) => { |
|
if (shouldBlockNavigation) { |
|
event.preventDefault(); |
|
event.returnValue = ''; |
|
} |
|
}; |
|
|
|
const nextNavigationHandler = (url) => { |
|
if (shouldBlockNavigation) { |
|
if (!window.confirm('Navigate away? Changes you made may not be saved.')) { |
|
Router.events.emit('routeChangeError') |
|
// eslint-disable-next-line no-throw-literal |
|
throw "Abort route change by user's confirmation." |
|
} |
|
} |
|
}; |
|
|
|
window.addEventListener('beforeunload', nativeBrowserHandler); |
|
Router.events.on("beforeHistoryChange", nextNavigationHandler); |
|
|
|
// On component unmount, remove the event listener |
|
return () => { |
|
window.removeEventListener('beforeunload', nativeBrowserHandler); |
|
Router.events.off("beforeHistoryChange", nextNavigationHandler); |
|
}; |
|
}, [shouldBlockNavigation]); |
|
|
|
// Relevent code end |
|
|
|
return <div>Your Component</div> |
|
|
|
} |
I'd used one of the previous (pretty messy and hacky) solutions but that suddenly stopped since upgrading to Next 13. So, I came back to the issue and found this much cleaner solution.
However, I do have an issue that I'm hoping you can help me with. In my _app.ts I am using the router events for my loading animation, like so:
This seems to take precedence over your event handlers on the component. So,
I'm pretty new to all this, so please let me know if my loading animation code is not a best practice. Otherwise, what could be a possible solution?