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

298 lines
5.6 KiB
Markdown
Executable File

# React Router(V6)
[Tutorial v6.8.1 | React Router](https://reactrouter.com/en/main/start/tutorial)
> v6不兼容 v5, 变化较大
```sh
# 安装最新版v6
pnpm add react-router-dom
```
## helloRouter
```jsx
/**
* react router 使用步骤
* 1. 安装react-router-dom包
* 2. 在main.jsx 中引入BrowserRouter组件
* 3. 将BrowserRouter设置为根组件
* 4. 在App.jsx 中引入 Routes, Route 组件
* 5. 用 Routes组件包裹Route组件
*/
```
```jsx
/**
* Routes v6新增的组件
* 类似v5的Switch, 都是用于Route的容器
* Routes中的Route只有一个会被匹配 不用加exact了
* v6中 component render children 都不能用了
* 改为element指定要挂载的组件
*/
```
```jsx
// @/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>
)
```
```jsx
// @/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
```
```jsx
// @/components/Home.jsx
const Home = ()=>{
return (
<h2>主页</h2>
)
}
export default Home
```
```jsx
// @/components/About.jsx
const About = ()=>{
return (
<div>
<h2>关于我们</h2>
</div>
)
}
export default About
```
```jsx
// @/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
```
## 钩子
```jsx
// 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
```jsx
/**
* V6默认严格匹配,想取消,可以 path='/about/*'
* Outlet 用来表示嵌套路由中的组件
* 当路径匹配成功, Outlet表示嵌套路由中的组件
* 当匹配失败, Outlet相当于不存在
* 有点像vue的插槽
*/
```
```jsx
// @/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
```
```jsx
// @/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 组件
```jsx
// 默认使用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
```jsx
<NavLink
to="/about/word"
style={({ isActive }) => {
return isActive ? { backgroundColor: 'yellow' } : null
}}>
about/word
</NavLink>
```
2. 使用className
```jsx
import classes from './Menu.module.css'
// ...
<NavLink
to="/about/hello"
className={({isActive}) => {
return isActive ? classes.active : ''
}}>
about/hello
</NavLink>
```
```css
/* Menu.module.css */
a.active {
background-color: green;
}
```
## 高阶组件实现路由守卫
```jsx
// 没有登录访问权限页面,跳转登录页面
<Route
path="/profile"
element={isLogin ? <Profilepage /> : <Navigate replace to="/login" />}
/>
```
提取成组件
```jsx
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
```
```jsx
<Route
path="/profile"
element={<NeedAuth><Profilepage /></NeedAuth>}
/>
```
想登录成功后返回原来的页面, 使用 useLocation 和 Navigate state 来传递和获取 path
```jsx
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
```