跳到主要内容

react-router-dom v6.3 切换路由前确认

· 阅读需 2 分钟
TransonQ
Front-end Engineer

背景​

时间: 2022-09-21

截止此篇博客日期, react-router-dom v6.2 版本里面没有了 <Prompt> , 这里查看官方文档迁移说明

有两个解决方式:

  • 降级 v5 / 6.0.0-alpha.5(有 usePrompt 和 useBlocker)
  • 自定义 hooks

自定义 hooks​

/src/hooks//useBlocker.js
import React from 'react'
import { UNSAFE_NavigationContext } from 'react-router-dom'

export function useBlocker(blocker, when = true) {
const navigator = React.useContext(UNSAFE_NavigationContext).navigator

React.useEffect(() => {
if (!when) return

const unblock = navigator.block((tx) => {
const autoUnblockingTx = {
...tx,
retry() {
unblock()
tx.retry()
},
}

blocker(autoUnblockingTx)
})

return unblock
}, [navigator, blocker, when])
}
/src/hooks/useCallbackPrompt.js
import { useCallback, useEffect, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { useBlocker } from './useBlocker'

export function useCallbackPrompt(when) {
const navigate = useNavigate()
const location = useLocation()
const [showPrompt, setShowPrompt] = useState(false)
const [lastLocation, setLastLocation] = useState(null)
const [confirmedNavigation, setConfirmedNavigation] = useState(false)

const cancelNavigation = useCallback(() => {
setShowPrompt(false)
}, [])

const handleBlockedNavigation = useCallback(
(nextLocation) => {
if (
!confirmedNavigation &&
nextLocation.location.pathname !== location.pathname
) {
setShowPrompt(true)
setLastLocation(nextLocation)
return false
}
return true
},
// added location to the handleBlockedNavigation hook. The location object changes and the location.pathname value remains outdated.
[confirmedNavigation, location]
)

const confirmNavigation = useCallback(() => {
setShowPrompt(false)
setConfirmedNavigation(true)
}, [])

useEffect(() => {
if (confirmedNavigation && lastLocation) {
navigate(lastLocation.location.pathname)

// Clean-up state on confirmed navigation
setConfirmedNavigation(false)
}
}, [confirmedNavigation, lastLocation])

useBlocker(handleBlockedNavigation, when)

return [showPrompt, confirmNavigation, cancelNavigation]
}

使用方式​

const [isEditing, setIsEditing] = usestate(false)

// 业务逻辑,

const [showPrompt, confirmNavigation, cancelNavigation] = useCallbackPrompt(isEditing)

// 自行封装一个离开当前路有前的 modal , 假设为 BeforeLeaveModal

<BeforeLeaveModal
showDialog={showPrompt} // boolean 此 modal 是否显示
cancelNavigation={cancelNavigation} // function 点击取消离开路由回调
confirmNavigation={confirmNavigation} // function 点击确认离开路由的回调
/>

续更​

2022-10-31

由于在 react-router-dom v6.4 更新后 UNSAFE_NavigationContext被废弃, 暂时无法使用上述的钩子. 如果想继续停留在 v6 版本, 建议先让版本保持在 v6.3.0