react-router-dom v6.3 切换路由前确认
· 阅读需 2 分钟
背景
时间: 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
