分享一个React 基础应用:分享实现路由返回拦截的三种方式。
最近项目为了避免用户误操作导致数据丢失,增加返回拦截功能,但是之前由于qiankun
的报错导致这个功能一直有一些问题,所以专门独立搞了一个专题研究在react中各种方式实现这个功能,组内小伙伴使用的是prompt
方式,实现的还是比较方便,我打算探索另外别的方式实现,想用hooks
的方式实现
{#_label1}
技术栈: {#heading-1}
-
react17
-
react-router5
{#_label2}
方法一:使用history.block方式实现 {#heading-2}
场景:需要灵活定制弹框样式,需要比对跳转地址和当前地址
不足:还需要手动放行,并且手动实现跳转
函数有两个参数,第一个是一个对象,包含要跳转的地址等信息,第二个是触发的动作,包含三个值POP,REPLACE,PUSH
,可以根据这三个类型,定制开发一些业务逻辑,函数的返回值是解除限制函数,比如只想回退拦截,手动点击跳转不拦截,就可以判断action是POP
的时候返回false
实现方式如下:
useEffect(() => {
//这里要缓存函数返回的方法,返回的函数是解除限制函数,想解除直接调用一下
unblockRef.current = history.block((tx, action) => {
setTargetPathname(tx.pathname)//缓存目标路径 之所以缓存,是为了弹框点击确认的时候可以获取到
const isBlock = history.location.pathname != tx.pathname//当跳转的目标地址不是当前地址,则拦截,只要不在当前页面就拦截
setOpen(isBlock)
return isBlock == true ? false : true ////返回false是阻塞, 返回true是不阻塞 注意这里和block的值正好相反
})
return () => {
unblockRef.current()// 组件卸载之后要解除限制
}
}, [history])
下面定义一个自定义的弹框,直接用原生dialog元素写个
<dialog className="dialog" open={open}>
<p>确认离开么<br /></p>
<button onClick={cancel}>取消</button>
<button onClick={handleConfirm}>确定</button>
</dialog>
注意,当点击确认的时候,需要先解除限制,然后再跳转一下之前要跳转的链接,官方说有个retry的方法,我一直没有找到该方法,也可能我的react-router版本低吧
const handleConfirm = () => {
setOpen(false)
if (unblockRef.current) {
unblockRef.current();//释放限制
}
history.push(targetPathname)//从新跳转之前要跳转的页面
}
方法二:使用prompt结合message函数实现自定义弹框 {#heading-3}
场景: 需要通过开关控制是否拦截,并需要自定义ui,需要比对跳转地址
不足:当点击通过的时候,如果不延时会存在状态更新不及时导致不能正常跳转
prompt接受一个when的参数,true代表拦截 message可以是一个字符串或者是一个函数,如果是字符串则调用系统默认弹框, 如果想自定义弹框则需要在函数里控制,函数返回boolean值,false代表拦截,true代表放
<Prompt when={isBlock} message={(location) => {
if (location.pathname !== history.location.pathname) {
setTargetPathname(location.pathname)
setIsBlock(true)
setOpen(true)
return false//返回false是拦截
} else {
setIsBlock(false)
return true//true是放行
}
}}></Prompt>
当想点击确认跳转的时候,要先解除限制,然后再手动跳转,由于状态可能更新不及时,所以设置几百毫秒之后再跳转
const handleConfirm = () => {
setIsBlock(false)
setOpen(false)
//需要等待锁释放之后才能跳转
setTimeout(() => {
history.push(targetPathname)//从新跳转之前要跳转的页面
console.log('确认')
}, 100)
}
const cancel = () => {
setOpen(false)
}
方法三:基于prompt,修改路由getUserConfirmation实现自定义弹框拦截 {#heading-4}
场景:需要全局统一样式,弹框样式固定,需要灵活控制拦截条件
不足:
由于拦截弹框是在全局进行拦截,所以如果不同页面要求不同拦截弹框不太好实现
不太好判断要跳转到的地址和当前地址比对
页面中只需要写如下,通过一个开关控制是否拦截,给一个错误的提示
<Prompt when={isBlock} message={'自定义---确定你要返回么!!!!!'}></Prompt>
然后再路由定义的时候增加如下,增加一个getUserConfirmation配置,用来获取prompt传过来的拦截消息,并缓存callback函数,这个函数用来控制是否放行
<Router
getUserConfirmation={(message, callback) => {
callbackRef.current = callback
setMessage(message)
setOpen(true)
}
}
>
当点击确认跳转则调用callback(true)
,点击取消则调用callback(false)
,路径地址就变回来了, callback(true)
不需要再手动跳转,会自动跳转
const handleConfirm = () => {
setOpen(false)
callbackRef.current?.(true)
}
const cancel = () => {
callbackRef.current?.(false)
setOpen(false)
}
大家试试吧!