All files / shared / hooks useDirtyGuard.ts

100% Statements 15/15
100% Branches 10/10
100% Functions 7/7
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46                                        7x   3x 1x 3x       7x 7x     1x     4x 4x     7x   1x 1x      
import { useEffect } from 'react'
import { useBlocker } from 'react-router-dom'
 
export interface DirtyGuardOptions {
  isDirty: boolean
  submitted: boolean
  ownPathname?: string
}
 
export interface DirtyGuardResult {
  isBlocked: boolean
  confirmNavigation: () => void
  cancelNavigation: () => void
}
 
export function useDirtyGuard({
  isDirty,
  submitted,
  ownPathname,
}: DirtyGuardOptions): DirtyGuardResult {
  const blocker = useBlocker(
    ({ currentLocation, nextLocation }) => {
      if (!isDirty || submitted) return false
      const current = ownPathname ?? currentLocation.pathname
      return current === nextLocation.pathname
    },
  )
 
  useEffect(() => {
    if (!isDirty || submitted) return
 
    function handleBeforeUnload(e: BeforeUnloadEvent) {
      e.preventDefault()
    }
 
    window.addEventListener('beforeunload', handleBeforeUnload)
    return () => window.removeEventListener('beforeunload', handleBeforeUnload)
  }, [isDirty, submitted])
 
  return {
    isBlocked: blocker.state === 'blocked',
    confirmNavigation: () => blocker.proceed?.(),
    cancelNavigation: () => blocker.reset?.(),
  }
}