Skip to content

Instantly share code, notes, and snippets.

@saitergun
Last active August 15, 2024 13:37
Show Gist options
  • Save saitergun/e06b4c48ff328c61efc2e2b242cb4690 to your computer and use it in GitHub Desktop.
Save saitergun/e06b4c48ff328c61efc2e2b242cb4690 to your computer and use it in GitHub Desktop.
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { NavigateOptions } from 'next/dist/shared/lib/app-router-context.shared-runtime'
export default function usePageLeavingConfirm(showConfirm: boolean) {
const router = useRouter()
const handleAnchorClick = (e: MouseEvent) => {
if (e.button !== 0) return // only handle left-clicks
const targetUrl = (e.currentTarget as HTMLAnchorElement).href
const currentUrl = window.location.href
if (targetUrl !== currentUrl && window.onbeforeunload) {
// @ts-ignore
const res = window.onbeforeunload()
if (!res) e.preventDefault()
}
}
const addAnchorListeners = () => {
const anchorElements = document.querySelectorAll('a[href]') as NodeListOf<HTMLAnchorElement>
anchorElements.forEach((anchor) => anchor.addEventListener('click', handleAnchorClick))
}
useEffect(() => {
const mutationObserver = new MutationObserver(addAnchorListeners)
mutationObserver.observe(document.body, { childList: true, subtree: true })
addAnchorListeners()
return () => {
mutationObserver.disconnect()
const anchorElements = document.querySelectorAll('a[href]') as NodeListOf<HTMLAnchorElement>
anchorElements.forEach((anchor) => anchor.removeEventListener('click', handleAnchorClick))
}
}, [])
useEffect(() => {
const beforeUnloadHandler = (e: BeforeUnloadEvent) => {
e.preventDefault()
e.returnValue = '' // required for Chrome
}
const handlePopState = (e: PopStateEvent) => {
if (showConfirm) {
const confirmLeave = window.confirm('You have unsaved changes. Are you sure you want to leave?')
if (!confirmLeave) {
e.preventDefault()
window.history.pushState(null, '', window.location.href)
}
}
}
if (showConfirm) {
window.addEventListener('beforeunload', beforeUnloadHandler)
window.addEventListener('popstate', handlePopState)
} else {
window.removeEventListener('beforeunload', beforeUnloadHandler)
window.removeEventListener('popstate', handlePopState)
}
return () => {
window.removeEventListener('beforeunload', beforeUnloadHandler)
window.removeEventListener('popstate', handlePopState)
}
}, [showConfirm])
useEffect(() => {
const originalPush = router.push
router.push = (url: string, options?: NavigateOptions) => {
if (showConfirm) {
const confirmLeave = window.confirm('You have unsaved changes. Are you sure you want to leave?')
if (confirmLeave) originalPush(url, options)
} else {
originalPush(url, options)
}
}
return () => {
router.push = originalPush
}
}, [router, showConfirm])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment