乐趣区

关于react-router:Reactrouter-V6-拦截-路由跳转

指标实现成果:拦挡路由变动做自定义解决,比方在一个表单尚未填写实现,用户就要来到以后页面此时须要给用户做一个揭示,如下图所示

先说一下背景常识: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 时提供了两个 hooks useBlocker/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 的降级可能还会加回来该性能,上述代码仅供参考
退出移动版