devStandard/docs/learning/7-react/3-React Router(V6).md
2025-03-29 14:35:49 +08:00

5.6 KiB
Executable File

React Router(V6)

Tutorial v6.8.1 | React Router

v6不兼容 v5, 变化较大

# 安装最新版v6
pnpm add react-router-dom

helloRouter

/**
 * react router 使用步骤
 * 1. 安装react-router-dom包
 * 2. 在main.jsx 中引入BrowserRouter组件
 * 3. 将BrowserRouter设置为根组件
 * 4. 在App.jsx 中引入 Routes, Route 组件
 * 5. 用 Routes组件包裹Route组件
 */
/**
* Routes v6新增的组件
*   类似v5的Switch, 都是用于Route的容器
*   Routes中的Route只有一个会被匹配 不用加exact了
* v6中 component render children 都不能用了
*   改为element指定要挂载的组件
*/
// @/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import {BrowserRouter as Router} from 'react-router-dom'

ReactDOM.createRoot(document.getElementById('root')).render(
  <Router>
    <App />
  </Router>
)
// @/App.jsx
import { Routes, Route } from 'react-router-dom'
import Menu from './components/Menu'
import Home from './components/Home'
import About from './components/About'

function App() {
  return (
    <div className="App">
      <Menu />
      <Routes>
        <Route path='/' element={<Home />} />
        <Route path='/about' element={<About />} />
      </Routes>
    </div>
  )
}

export default App
// @/components/Home.jsx
const Home = ()=>{
  return (
    <h2>主页</h2>
  )
}

export default Home
// @/components/About.jsx
const About = ()=>{
  return (
    <div>
      <h2>关于我们</h2>
    </div>
  )
}

export default About
// @/components/Menu.jsx
import { Link } from 'react-router-dom'

const Menu = () => {
  return (
    <div>
      <ul>
        <li>
          <Link to="/">主页</Link>
        </li>
        <li>
          <Link to="/about">关于</Link>
        </li>
      </ul>
    </div>
  )
}

export default Menu

钩子

// useParams() 不变
// useLocation() 不变

// [x] useRouteMatch() 没有了
// useMatch(path) 检查当前url是否匹配某个路由,需要传参, 例如 useMatch('/about'),不匹配返回null, 匹配返回对象

// [x] useHistory() 没有了
// useNavigage() 获取一个用来跳转的函数
// ...
import {useNavigate} from 'react-router-dom'
// ...
const nav = useNavigate()
const clickHandler = ()=>{
  nav('/about') // 默认使用push跳转,有历史记录
  nav('/about',{replace:true}) // 使用replace跳转
}

嵌套路由+ Outlet

/**
* V6默认严格匹配,想取消,可以 path='/about/*'
* Outlet 用来表示嵌套路由中的组件
*   当路径匹配成功, Outlet表示嵌套路由中的组件
*   当匹配失败, Outlet相当于不存在
*   有点像vue的插槽
*/
// @/App.jsx
// Route嵌套Route
// '/about'可以省略成'about', '/about/hello'省略成'hello'

import { Routes, Route } from 'react-router-dom'
import Menu from './components/Menu'
import Home from './components/Home'
import About from './components/About'
import Hello from './components/Hello'
import Word from './components/Word'

function App() {
  return (
    <div className="App">
      <Menu />
      <Routes>
        <Route path='/' element={<Home />} />
        <Route path='about' element={<About />} >
          <Route path='hello' element={<Hello />} />
          <Route path='word' element={<Word />} />
        </Route>
      </Routes>
    </div>
  )
}

export default App
// @/components/About
// 添加Outlet组件
// 会智能匹配 hello组件或者word组件

import { Outlet } from 'react-router-dom'

const About = ()=>{
  return (
    <div>
      <h2>关于我们</h2>
      <Outlet />
    </div>
  )
}

export default About

Navigate组件

V5使用 Redirect 重定向, V6使用 Navigate 组件

// 默认使用push跳转
{!isLogin && <Navigate to="/login" />}

// 添加replace使用replace跳转
{!isLogin && <Navigate replace to="/login" />}

// 传递参数
{!isLogin && <Navigate replace to="/login" state={arg:'test'}/>} // 在跳转后的页面用useLocation()可以拿到

NavLink组件

V5的 activeStyle activeClassName 取消了 V6 改成style={()=>{}}

  1. 使用style
  <NavLink
    to="/about/word"
    style={({ isActive }) => {
      return isActive ? { backgroundColor: 'yellow' } : null
    }}>
    about/word
  </NavLink>
  1. 使用className
import classes from './Menu.module.css'
// ...
<NavLink
  to="/about/hello"
  className={({isActive}) => {
    return isActive ? classes.active : ''
  }}>
  about/hello
</NavLink>
/* Menu.module.css */
a.active {
  background-color: green;
}

高阶组件实现路由守卫

// 没有登录访问权限页面,跳转登录页面
<Route
  path="/profile"
  element={isLogin ? <Profilepage /> : <Navigate replace to="/login" />}
/>

提取成组件

import { useSelector } from 'react-redux'
import { Navigate } from 'react-router-dom'

const NeedAuth = props => {
  const { isLogin } = useSelector(state => state.auth)
  return isLogin ? props.children : <Navigate replace to="/login" />
}

export default NeedAuth
<Route
  path="/profile"
  element={<NeedAuth><Profilepage /></NeedAuth>}
/>

想登录成功后返回原来的页面, 使用 useLocation 和 Navigate state 来传递和获取 path

import { useSelector } from 'react-redux'
import { Navigate,useLocation } from 'react-router-dom'

const NeedAuth = props => {
  const location = useLocation()
  const { isLogin } = useSelector(state => state.auth)
  return isLogin ? props.children : <Navigate replace to="/login" state={{prevLocation:location}} />
}

export default NeedAuth