共计 2345 个字符,预计需要花费 6 分钟才能阅读完成。
指标实现成果:拦挡路由变动做自定义解决,比方在一个表单尚未填写实现,用户就要来到以后页面此时须要给用户做一个揭示,如下图所示
先说一下背景常识:React-router 是由三个库一起组成的 history、react-router、react-router-dom
咱们平时须要用到的是 react-router-dom
v5 版本实现路由拦挡
-
以前在应用 v5 版本时,是这样实现路由拦挡的
// 文档:https://v5.reactrouter.com/core/api/Prompt <Prompt when={boolean} // 组件何时激活 message={(location, action) => { // 做一些拦挡操作 location 要返回的路由,此时能够先保留下来后续应用 // return false 勾销跳转 比方此时弹起一个自定义弹窗,// return true 容许跳转 }} />
v6 版本实现
- v6 版本没有了
Prompt
组件,Google 搜寻之后找到了这个 stackoverflow v6 beta 时提供了两个 hooksuseBlocker
/usePrompt
能够用来实现路由拦挡,然而到正式版的时候这两个 hook 就被移除了,这个 issue 外面有探讨,这里有人找出了解决方案就是把删除的这两个 hooks 再加回去 😂 - 其实路由拦挡性能次要是用到了
history
库外面的block
办法,这里是相干代码 -
histoy block 文档
history.block will call your callback for all in-page navigation attempts, but for navigation that reloads the page (e.g. the refresh button or a link that doesn’t use history.push) it registers a beforeunload handler to prevent the navigation. In modern browsers you are not able to customize this dialog. Instead, you’ll see something like this (Chrome):
- 简略的翻译下就是
histoy.block
会阻止页面中的所有导航并调用 callback,然而间接敞开 tab 页或是刷新会注册beforeunload
事件继而触发浏览器的默认询问弹窗,不反对去除默认弹框,我上面采纳了一种 hack 的方法来去除 默认询问弹框 -
残缺代码
import {History, Transition} from 'history' import {useContext, useEffect} from 'react' import {UNSAFE_NavigationContext as NavigationContext} from 'react-router-dom' type ExtendNavigator = Navigator & Pick<History, 'block'> export function useBlocker(blocker: (tx: Transition) => void, when = true) {const { navigator} = useContext(NavigationContext) useEffect(() => {if (!when) return // 如不须要刷新页面或敞开 tab 时勾销浏览器询问弹窗,上面的绑定事件则不须要 window.addEventListener('beforeunload', removeBeforeUnload) const unblock = (navigator as any as ExtendNavigator).block(tx => { const autoUnblockingTx = { ...tx, retry() {unblock() tx.retry()}, } blocker(autoUnblockingTx) }) // 因为无奈间接 remove history 库中绑定的 beforeunload 事件,只能本人在绑定一个 beforeunload 事件(在原事件之前),触发时调用 unblock // function removeBeforeUnload() {unblock() } return () => {unblock() window.removeEventListener('beforeunload', removeBeforeUnload) } }, [when]) }
-
应用
useBlocker
export default function UnsavedPrompt({when}: Iprops): JSX.Element {const [open, setOpen] = useState(false) const blockRef = useRef<any>(null) useBlocker(tx => {setOpen(true) blockRef.current = tx }, when) return ( <Modal open={open} toggle={() => setOpen(false)} onCancel={() => blockRef.current?.retry()} onOk={() => setOpen(false)} > <p className='text-center text-light-700 text-sm'> You have unsaved change, exit without saving? </p> </Modal> ) }
留神
- 书写本文的工夫是 2022-08-11,
react-router/react-router-dom
的最新版本为6.3.0
,后续可能随着 react-router-dom 的降级可能还会加回来该性能,上述代码仅供参考
正文完
发表至: react-router
2022-08-11