mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 18:11:51 +08:00
## Problem Since the refactor done in #43900, the `<PreventNavigationOnUnsavedChanges>` does not bring much value. ## Solution Remove `PreventNavigationOnUnsavedChanges` and update consumers to leverage `usePreventNavigationOnUnsavedChanges` and `DiscardChangesConfirmationDialog` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved the architecture of unsaved-changes navigation handling across multiple features. Components now use a more modular hook-based approach for better code organization and consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
91 lines
2.8 KiB
TypeScript
91 lines
2.8 KiB
TypeScript
import { useRouter } from 'next/router'
|
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
|
|
import { BASE_PATH } from '@/lib/constants'
|
|
|
|
interface UsePreventNavigationOnUnsavedChangesOptions {
|
|
/*
|
|
* Boolean indicating whether there are changes that would be lost if users navigate to another
|
|
* page or close the browser tab
|
|
*/
|
|
hasChanges: boolean
|
|
}
|
|
|
|
interface UsePreventNavigationOnUnsavedChangesReturn {
|
|
/*
|
|
* Cancel the navigation and keep the changes
|
|
*/
|
|
handleCancelNavigation: () => void
|
|
/*
|
|
* Confirm the navigation and lose the changes
|
|
*/
|
|
handleConfirmNavigation: () => void
|
|
/*
|
|
* Boolean indicating whether UI to request users confirmation for the navigation should be
|
|
* displayed
|
|
*/
|
|
shouldConfirmNavigation: boolean
|
|
}
|
|
|
|
/*
|
|
* Hook that prevents navigation when users could lose their changes.
|
|
* It prevents both NextJS and browser navigation (such as when closing the tab)
|
|
*/
|
|
export const usePreventNavigationOnUnsavedChanges = ({
|
|
hasChanges,
|
|
}: UsePreventNavigationOnUnsavedChangesOptions): UsePreventNavigationOnUnsavedChangesReturn => {
|
|
const router = useRouter()
|
|
const [navigateUrl, setNavigateUrl] = useState<string>()
|
|
const [confirmNavigate, setConfirmNavigate] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
|
if (hasChanges) {
|
|
e.preventDefault()
|
|
e.returnValue = '' // deprecated, but older browsers still require this
|
|
}
|
|
}
|
|
|
|
const handleBrowseAway = (url: string) => {
|
|
if (hasChanges && !confirmNavigate) {
|
|
setNavigateUrl(url)
|
|
throw 'Route change declined' // Just to prevent the route change
|
|
return
|
|
}
|
|
setNavigateUrl(undefined)
|
|
}
|
|
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
router.events.on('routeChangeStart', handleBrowseAway)
|
|
|
|
return () => {
|
|
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
router.events.off('routeChangeStart', handleBrowseAway)
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [confirmNavigate, hasChanges])
|
|
|
|
const handleCancelNavigation = useCallback(() => {
|
|
setNavigateUrl(undefined)
|
|
}, [])
|
|
|
|
const handleConfirmNavigation = useCallback(() => {
|
|
setConfirmNavigate(true)
|
|
let urlToNavigate = navigateUrl ?? '/'
|
|
if (BASE_PATH && urlToNavigate.startsWith(BASE_PATH)) {
|
|
urlToNavigate = urlToNavigate.slice(BASE_PATH.length) || '/'
|
|
}
|
|
if (!urlToNavigate.startsWith('/')) urlToNavigate = `/${urlToNavigate}`
|
|
setNavigateUrl(undefined)
|
|
router.push(urlToNavigate)
|
|
}, [navigateUrl, router])
|
|
|
|
return useMemo(
|
|
() => ({
|
|
handleCancelNavigation,
|
|
handleConfirmNavigation,
|
|
shouldConfirmNavigation: !!navigateUrl,
|
|
}),
|
|
[navigateUrl, handleCancelNavigation, handleConfirmNavigation]
|
|
)
|
|
}
|