react router 路由跳转

228 阅读1分钟

路由跳转改变按钮状态

我们想点击Link(a 标签)按钮时,不仅改变路由,并且给它一个选中的状态

//封装一个自定义组件MyLink.tsx
import {Link, useMatch, useResolvedPath} from 'react-router-dom'

function MyLink({children, to, className ,...props}: any) {
  let resolved = useResolvedPath(to)
  //useResolvedPath()方法会解析当前路由对应的 to 属性,并生成一个 path 对象,然后我们获取这个对象
  let active = useMatch({path: resolved.pathname, end: true})
  //useMatch 将location.pathname 和传入的 path 做匹配,如果成功返回一个对象,失败返回 null,我们就可以利用这个特性,做点击改变状态了
  //end 为 true 表示从后往前匹配

  return(
    //active 为对象时,布尔值为true, 为 null 时布尔值为 false
    //className利用组件的 props 传进来
    <Link to={to} className={active ?  `active ${className}` : className}></Link>
  )
}

在用这个组件代替掉我们原来的 Link 组件就可以实现点击按钮路由跳转时改变按钮状态了

//Button.tsx
import MyLink from './Mylink'

export default function Button() {

//构建一个数组来遍历生成按钮
const btn = [
  {
    path: 'btn1',
    text: 'btn1'
  }, {
    path: 'btn2',
    text: 'btn2'
  }, {
    path: 'btn3',
    text: 'btn3'
  }
]
  retrun(
    <div>
      {btn.map((item) => (
          <MyLink key={item.path} to={item.path} className={'btn'}>{item.text}</MyLink>
        ))}
    </div>
  )
}

ok,完成了,点击按钮能实现跳转了,而且该按钮状态也改变了。

一个路由改变多个按钮状态

但是出现了一个问题,就是当点击三级路由的时候,二级路由的选中状态消失了,这不是我们所希望的,我们希望二级路由和它下面的三级路由都选中
我们把useMatch 中的 end 改成 false 即可多选,因为它会从前往后匹配,将 location.pathname 中包含我们传入的to属性匹配成功
但是首页的'/'属性不管是那个路由都能匹配成功,所以要专门对首页进行一些判断

//MyLink.tsx
//当有按钮处于选中状态且不是首页时,将首页的选中状态取消掉
if(active && location.pathname !== '/' && to === '/') {
  active = null
}

这样我们选择其他二级按钮的时候,就不会把首页也选中了,还可以把三级按钮给选中

切换路由时,缓存切换前路由的状态

就是当我们切换二级路由时,我们想要下次再切换到这个路由来时保持上次所选中的三级路由为选中状态

//Button.tsx
import MyLink from './Mylink'
import {useNavigate, Outlet} from 'react-router-dom'
import { useEffect, useRef } from 'react'

//设置一个默认路由,单页跳转到该组件路由时,将设置
let prepath = '/button/btn1'
export default function Button() {
  var navigate = useNavigate()
  //设置一个默认值,点击按钮时修改
  const path = useRef(prepath)

//构建一个数组来遍历生成按钮
  const btn = [
    {
      path: 'btn1',
      text: 'btn1'
    }, {
      path: 'btn2',
      text: 'btn2'
    }, {
      path: 'btn3',
      text: 'btn3'
    }
  ]

useEffect(() => {
//当页面渲染完成之后,如果路由为'/button',就跳转到上一次 prepath 缓存的三级路由,如果是第一次会使用prepath 的默认值
  if(loaction.pathname = '/button') {
    navigate(prepath)
  }
  //useEffect里的 return,清除该组件之前会调用一次
  return () => {
    prepath = path.current
  }
},[]) 
  retrun(
    <div>
      {btn.map((item) => (
          <MyLink 
            key={item.path} 
            to={item.path} 
            className={'btn'}
            //点击按钮时将该按钮的 path 传递给 path.current
            //prepath 在这个组件外面由于闭包的特性他会缓存 path.current 的值不会被清除
            onClick={() => {path.current = `/button/${item.path}`}}
          >{item.text}</MyLink>
        ))}
    </div>
  )
}

点击按钮不跳转

这里又出现了一个问题,我们缓存了三级路由后,切换路由回到 button 路由时,确实会跳转到我们之前访问的那个三级路由并选中该三级路由,但是当我们再次点击 button 按钮时,会将路由改成'/button',三级路由就不会被选中了。
所以为了防止多次点击,我们要判断一下是否为选中状态,如果为选中状态就阻止页面跳转

//MyLink.tsx
//给 Link添加一个点击事件
  function __handler(e: any) {
    if (active) {
    //这个方法会阻止默认事件的发生
      e.preventDefault()
    }
    //判断是否传入了点击事件,三级路由的点击事件也需要执行,如果不写虽然也可以改变三级路由,但是缓存的路由会有偏差
    onClick && onClick()
  }

这样就可以避免再次点击button按钮时将三级路由给替换掉了。