【第九章-React Router 6】

【第九章-React Router 6】

  • 一、概述
  • 二、一级路由
    • [1. 准备工作](#1. 准备工作)
    • [2. 安装](#2. 安装)
    • [3. 配置路由](#3. 配置路由)
      • [3.1 引入BrowserRouter包裹整个路由器](#3.1 引入BrowserRouter包裹整个路由器)
      • [3.2 编写路由链接和注册路由](#3.2 编写路由链接和注册路由)
      • [3.3. 核心变化总结](#3.3. 核心变化总结)
  • 三、重定向
    • [1. Navigate组件的使用](#1. Navigate组件的使用)
    • [2. Navigate组件的案例](#2. Navigate组件的案例)
    • [3. replace 模式](#3. replace 模式)
  • [四、NavLink 高亮](#四、NavLink 高亮)
    • [1. 基本使用与默认高亮](#1. 基本使用与默认高亮)
    • [2. 自定义类名(使用函数)](#2. 自定义类名(使用函数))
  • [五、 useRoutes 路由表](#五、 useRoutes 路由表)
    • [1. 核心概念](#1. 核心概念)
    • [2. 使用步骤与代码示例](#2. 使用步骤与代码示例)
  • 六、嵌套路由
    • [1. 准备工作](#1. 准备工作)
    • [2. 配置二级路由](#2. 配置二级路由)
    • [3. 子路由路径的三种写法](#3. 子路由路径的三种写法)
    • [4. end属性](#4. end属性)
  • [七. 路由的传参方式](#七. 路由的传参方式)
    • [1. 准备工作](#1. 准备工作)
      • [1.1 优化Message组件](#1.1 优化Message组件)
      • [1.2 配置三级路由](#1.2 配置三级路由)
    • [2. 通过 params 参数传参](#2. 通过 params 参数传参)
    • [3. 通过 search 参数传参](#3. 通过 search 参数传参)
    • [4. 通过 state 参数传参](#4. 通过 state 参数传参)
  • 八、编程式路由导航
    • [1. Navigate 组件与 useNavigate 钩子的区别](#1. Navigate 组件与 useNavigate 钩子的区别)
    • [2. 基本用法](#2. 基本用法)
    • [3. Navigate的其他传参方式](#3. Navigate的其他传参方式)
    • [4. 使用 Navigate 实现前进和后退](#4. 使用 Navigate 实现前进和后退)
  • 九、useInRouterContext
  • 十、useNavigationType
  • 十一、useOutlet
  • 十二、useResolvedPath

一、概述

1.React Router 以三个不同的包发布到 npm 上,它们分别为:

  • react-router:路由的核心库,提供了很多的:组件、钩子。
  • react-router-domt:包含react-router所有内容,并添加一些专门用于 DOM 的组件,例如<BrowserRouter>等。
  • react-router-native:包括react-router所有内容,并添加一些专门用于ReactNative的AP!,例如<NativeRouter>等。

2.与 React Router 5.x 版本相比,改变了什么?

  • 内置组件的变化:移除<switch/>,新增<Routes/>等。
  • 语法的变化:component={About} 变为 element={<About/>}等。
  • 新增多个hook:useParamsuseNavigateuseMatch等。
  • 官方明确推荐函数式组件了!!!

二、一级路由

1. 准备工作

我们还是继续使用之前讲解 React Router5 的代码为例。

App.jsx

javascript 复制代码
import Home from './pages/Home'
import About from './pages/About'
import './App.css';

function App() {
  return (
    <div classNameName="App">
      <div className="container mt-4" style={{maxWidth: '1000px'}}>
    <div className="row">
        <div className="col">
            <h1 className="header-title text-dark">React Router Demo</h1>
            <span className="text-muted"></span>
        </div>
    </div>

    <div className="row mt-4">
        <div className="col-3">
            <div className="list-group">
              <a href="./about.html" class="list-group-item list-group-item-action text-secondary">About</a>
              <a href="./home.html" class="list-group-item list-group-item-action active bg-primary border-primary">Home</a> 
            </div>
        </div>

        <div className="col-9 content-area">
        	<Home />
            <About />
        </div>
    </div>
</div>
    </div>
  );
}

export default App;

pages/About/index.jsx

javascript 复制代码
import React from 'react'

export default function Home() {
  return (
    <div>我是About的内容</div>
  )
}

pages/Home/index.jsx

javascript 复制代码
import React from 'react'

export default function About() {
  return (
    <div>我是About的内容</div>
  )
}

2. 安装

javascript 复制代码
npm install react-router-dom

3. 配置路由

3.1 引入BrowserRouter包裹整个路由器

index.js

javascript 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import {BrowserRouter} from 'react-router-dom';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

3.2 编写路由链接和注册路由

javascript 复制代码
import { NavLink, Routes, Route } from "react-router-dom";
...

<div className="list-group">
	{/* 路由链接 */}
	<NavLink to="/about" className="list-group-item list-group-item-action text-secondary">About</NavLink>
	<NavLink to="/home" className="list-group-item list-group-item-action text-secondary">Home</NavLink>
</div>

<div className="col-9 content-area">
	{/* 注册路由 */}
	<Routes>
		<Route path="/about" element={<About />} />
		<Route path="/home" element={<Home />} />
	</Routes>
</div>

3.3. 核心变化总结

  • <Routes> 替代 <Switch>:v6 中所有的 <Route> 必须被 <Routes> 包裹。
  • element 属性替代组件标签:指定渲染内容时使用 element = {<Home />},而不是旧版本的 component={Home} 或子节点。

注意:<Route casesensitive>属性用于指定匹配时是否区分大小写(默认为 false)。

javascript 复制代码
<Route caseSensitive path="/HOME" element={<Home />} />

若开启大小写匹配模式,则/home无法匹配到/HOME


三、重定向

在 react-router-dom v6 中,我们使用<Navigate>代替 <Redirect><Navigate> 是一个用于重定向的组件,当它被渲染时,会自动修改当前页面的路径,切换视图。

语法:<Navigate to="/xxx" />

javascript 复制代码
<Routes>
	<Route path="/about" element={<About />} />
	<Route path="/home" element={<Home />} />
	<Route path="/" element={<Navigate to="/about" />}></Route>
</Routes>

Navigate只要渲染就会引起视图的切换,为了深入理解Navigate,我们来实现一个小案例。

Home 组件添加一个sum值和点击按钮,当sum===2的时候,就自动跳转 About 组件。

src/pages/Home/index.jsx

javascript 复制代码
import React from 'react'
import { Navigate } from 'react-router-dom'

export default function Home() {
  const [sum, setSum] = React.useState(0)
  return (
    <div>
      <div>我是Home的内容</div>
      <div>
        {
          sum === 2 ? <Navigate to="/about" /> : <p>当前sum值是:{sum}</p>
        }
  
        <div>
          <button onClick={() => setSum(2)}>点我将sum变成2</button>
        </div>
      </div>
    </div>
  )
}

点击按钮,切换到About组件:

3. replace 模式

替换历史记录默认是 push 模式:

javascript 复制代码
<Navigate to="/about" replace={false}/>

如果希望覆盖当前历史记录,可以设置 replace: true

javascript 复制代码
<Navigate to="/about" replace={true}/>

在 react-router-dom v6 中,NavLink 组件默认会自动给当前激活的链接添加一个名为 active 的 CSS 类名,且废弃了 v5 中的 activeClassName 属性。

1. 基本使用与默认高亮

默认情况下,直接使用 NavLink 并编写 .active 样式即可实现高亮:

javascript 复制代码
  <NavLink to="/about" className="list-group-item list-group-item-action text-secondary">About</NavLink>
  <NavLink to="/home" className="list-group-item list-group-item-action text-secondary">Home</NavLink>

2. 自定义类名(使用函数)

但有的时候,我们的高亮的类名不一定是active,就需要将 className 属性写成一个回调函数,接收一个参数 { isActive },然后动态判断并返回自定义类名。

比如:我们定义高亮的类名为atguigu

css 复制代码
.atguigu {
  background-color: orange !important;
  color: #007bff !important;
}

然后我们在 className 的回调函数里结构isActive,若isActivetrue则代表是当前路由,需要添加类名atguigu

javascript 复制代码
<NavLink to="/about" className={({isActive}) => isActive ? "atguigu list-group-item list-group-item-action text-secondary":"list-group-item list-group-item-action text-secondary"}>About</NavLink>
<NavLink to="/home" className={({isActive}) => isActive ? "atguigu list-group-item list-group-item-action text-secondary":"list-group-item list-group-item-action text-secondary"}>Home</NavLink>

感觉这样的写法有点过于冗杂,我们可以把 className 里的逻辑封装成一个函数。

javascript 复制代码
function App() {
  // 是否添加高亮
  function computedClassName(isActive) {
    return isActive
      ? "atguigu list-group-item list-group-item-action text-secondary"
      : "list-group-item list-group-item-action text-secondary";
  }

  return (
    <div className="App">
      <div className="container mt-4" style={{ maxWidth: "1000px" }}>
        <div className="row">
          <div className="col">
            <h1 className="header-title text-dark">React Router Demo</h1>
            <span className="text-muted"></span>
          </div>
        </div>

        <div className="row mt-4">
          <div className="col-3">
            <div className="list-group">
              {/* 路由链接 */}
              <NavLink
                to="/about"
                className={({ isActive }) => computedClassName(isActive)}
              >
                About
              </NavLink>
              <NavLink
                to="/home"
                className={({ isActive }) => computedClassName(isActive)}
              >
                Home
              </NavLink>
            </div>
          </div>

          <div className="col-9 content-area">
            {/* 注册路由 */}
            <Routes>
              <Route path="/about" element={<About />} />
              <Route path="/home" element={<Home />} />
              <Route path="/" element={<Navigate to="/about" />}></Route>
            </Routes>
          </div>
        </div>
      </div>
    </div>
  );
}

这样就可以完成自定义类名的高亮效果了!!!


五、 useRoutes 路由表

在 react-router-dom v6 中,useRoutes 是一个钩子(Hook ),它用 JavaScript 对象(路由表) 代替了 <Routes><Route> 组件来配置路由。

1. 核心概念

  • 作用:以对象形式集中管理路由,使代码更清爽、更易于维护。
  • 位置限制 :必须在 <BrowserRouter> 或其他路由 Provider 组件内部执行。
  • 嵌套渲染 :子路由通过 <Outlet /> 组件在父组件中渲染。

2. 使用步骤与代码示例

  • 第一步:编写路由表文件

定义一个数组,每个对象代表一个路由,使用 pathelement

src/routes/index.js

javascript 复制代码
import { Navigate } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
// 定义路由规则
const routes = [
  { path: "/about", element: <About /> },
  { path: "/home", element: <Home /> },
  { path: "/", element: <Navigate to="/about" /> },
];

export default routes;
  • 第二步:在根组件或 App 中使用 useRoutes

引入路由表数组 ,并传给 useRoutes 钩子生成实际的路由结构,代替<Routes><Route>渲染到页面上。

App.jsx

javascript 复制代码
import { NavLink, useRoutes } from "react-router-dom";
// 1. 引入路由表数组
import routes from "./routes"

function App() {
	// 2. 根据路由表生成对应的路由组件
  const element = useRoutes(routes);

  return (
    <div className="App">
        <div className="row mt-4">
          <div className="col-3">
            <div className="list-group">
              {/* 路由链接 */}
              {...}
            </div>
          </div>

          <div className="col-9 content-area">
            {/* 注册路由 */}
            {/* 3.渲染匹配到的路由组件 */}
            {element}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

六、嵌套路由

1. 准备工作

首先,我们创建两个二级路由页面,分别是 NewsMessage

src/pages/Home/News/index.jsx

javascript 复制代码
export default function News() {
  return (
    <div
      className="tab-pane fade show active"
      id="news"
      role="tabpanel"
      aria-labelledby="news-tab"
    >
      <ul className="news-list">
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
      </ul>
    </div>
  );
}

src/pages/Home/Message/index.jsx

javascript 复制代码
export default function Message() {
  return (
    <div
      className="tab-pane fade show active"
      id="message"
      role="tabpanel"
      aria-labelledby="message-tab"
    >
      <ul className="news-list">
        <li>message001</li>
        <li>message002</li>
        <li>message003</li>
      </ul>
    </div>
  );
}

src/pages/Home/index.jsx

javascript 复制代码
import News from "./News";
import Message from "./Message";

export default function Home() {
  return (
    <div>
      <div>我是Home的内容</div>
      <div className="custom-card">
        <ul className="nav nav-tabs-custom" id="myTab" role="tablist">
          <li className="nav-item" role="presentation">
            <button
              className="nav-link active"
              id="news-tab"
              data-bs-toggle="tab"
              data-bs-target="#news"
              type="button"
              role="tab"
              aria-controls="news"
              aria-selected="true"
            >
              News
            </button>
          </li>
          <li className="nav-item" role="presentation">
            <button
              className="nav-link"
              id="message-tab"
              data-bs-toggle="tab"
              data-bs-target="#message"
              type="button"
              role="tab"
              aria-controls="message"
              aria-selected="false"
            >
              Message
            </button>
          </li>
        </ul>

        <div className="tab-content" id="myTabContent">
          <News />
          <Message />
        </div>
      </div>
    </div>
  );
}

2. 配置二级路由

  1. 使用路由表制定路由规则。

src/routes/index.js

javascript 复制代码
import { Navigate } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
// 1. 引入两个子路由组件
import News from "../pages/Home/News";
import Message from "../pages/Home/Message";

const routes = [
  { 
    path: "/about", 
    element: <About /> 
  }, { 
    path: "/home", 
    element: <Home />,
    // 2. 子路由配置,path直接写子路由的路径名字即可
    children: [
      {
        path: "news",
        element: <News />
      },
      {
        path: "message",
        element: <Message />
      },
    ] 
  }, { 
    path: "/", 
    element: <Navigate to="/about" /> 
  },
];

export default routes;
  1. 使用NavLink替换导航标签

src/pages/Home/index.jsx

javascript 复制代码
<ul className="nav nav-tabs-custom" id="myTab" role="tablist">
  <li className="nav-item" role="presentation">
    <NavLink to="news" className="nav-link">News</NavLink>
  </li>
  <li className="nav-item" role="presentation">
    <NavLink to="message" className="nav-link">Message</NavLink>
  </li>
</ul>
  1. 使用Outlet占位渲染子路由

在父组件中引入并放置 <Outlet />,子路由的内容会在该位置渲染。

src/pages/Home/index.jsx

javascript 复制代码
import { NavLink, Outlet } from "react-router-dom";

export default function Home() {
  return (
    <div>
      <div>我是Home的内容</div>
      <div className="custom-card">
        <ul className="nav nav-tabs-custom" id="myTab" role="tablist">
          <li className="nav-item" role="presentation">
            <NavLink to="news" className="nav-link">News</NavLink>
          </li>
          <li className="nav-item" role="presentation">
            <NavLink to="message" className="nav-link">Message</NavLink>
          </li>
        </ul>

        <div className="tab-content" id="myTabContent">
          {/* 指定路由组件呈现的位置 */}
          <Outlet />
        </div>
      </div>
    </div>
  );
}

页面效果:

3. 子路由路径的三种写法

  • 直接简写
javascript 复制代码
<NavLink to="news" className="nav-link">News</NavLink>
javascript 复制代码
{
	path: "news",
	element: <News />
}
  • 带上父组件路径(与V5一样)
javascript 复制代码
<NavLink to="/home/news" className="nav-link">News</NavLink>
javascript 复制代码
{
	path: "/home/news",
	element: <News />
}
  • 使用./xxx
javascript 复制代码
<NavLink to="./news" className="nav-link">News</NavLink>
javascript 复制代码
{
	path: "./news",
	element: <News />
}

注意:不管使用哪种写法,都不能直接使用/xxx的写法,不然会从根路径直接跳转。

javascript 复制代码
<NavLink to="/news" className="nav-link">News</NavLink>

4. end属性

React-router-dom v6 的 end 属性用于 NavLink 组件,用来控制当子路由被激活时,父路由链接是否仍然保持高亮状态。

我们当前的页面效果,当父子路由同时高亮:

在 App.jsx 中添加end属性:

javascript 复制代码
 <NavLink
    to="/home"
    end
    className="list-group-item list-group-item-action text-secondary"
  >
    Home
  </NavLink>

只有子路由保持高亮了:


七. 路由的传参方式

需求 】:我们想在 Message 组件下方创建一个 Detail 组件(也就是三级路由组件 ),点击 Message ,携带详情参数跳转到对应的详情页面。

案例效果】:

1. 准备工作

1.1 优化Message组件

  • 首先, Message 组件的每一项内容需要通过动态遍历状态数组来生成。

src/pages/Home/Message/index.jsx

javascript 复制代码
import React from 'react'

export default function Message() {
  const [messages] = React.useState([
    {
      id: '001',
      title: 'message001',
      content: '锄禾日当午',
    }, {
      id: '002',
      title: 'message002',
      content: '汗滴禾下土',
    }, {
      id:'003',
      title: 'message003',
      content: '谁知盘中餐',
    }, {
      id: '004',
      title: 'message004',
      content: '粒粒皆辛苦',
    },
  ])
  
  return (
    <div
      className="tab-pane fade show active"
      id="message"
      role="tabpanel"
      aria-labelledby="message-tab"
    >
      <ul className="news-list">
        {
          messages.map(item => {
            return (
              <li key={item.id}>{item.title}</li>
            )
          })
        }
      </ul>
    </div>
  );
}
  • Message 目录下创建 Detail 文件夹,新增 Detail 组件。

src/pages/Home/Message/Detail/index.jsx

javascript 复制代码
export default function Detail() {
  return (
    <div>
      <p>id:</p>
      <p>title:</p>
      <p>content:</p>
    </div>
  );
}

1.2 配置三级路由

  • 找到路由表,制定路由规则。

src/routes/index.js

javascript 复制代码
...
// 1. 引入 Detail 组件
import Detail from "../pages/Home/Message/Detail";

const routes = [
  { 
    path: "/about", 
    element: <About /> 
  }, { 
    path: "/home", 
    element: <Home />,
    children: [
      {
        path: "news",
        element: <News />
      },
      {
        path: "message",
        element: <Message />,
        // 2. 制定路由
        children: [
          {
            path: "detail",
            element: <Detail />
          }
        ]
      },
    ] 
  }, { 
    path: "/", 
    element: <Navigate to="/about" /> 
  },
];

export default routes;
  • 因为三级路由不需要高亮效果,所以使用Link替换导航标签。

src/pages/Home/Message/index.jsx

javascript 复制代码
import { Link } from "react-router-dom";

...

<ul className="news-list">
	{messages.map((item) => {
		return (
			<li key={item.id}>
			 	{/* 路由链接 */}
				<Link to="detail">{item.title}</Link>
			</li>
		);
	})}
</ul>
  • 使用Outlet占位渲染子路由。

src/pages/Home/Message/index.jsx

javascript 复制代码
import React from "react";
import { Link, Outlet } from "react-router-dom";
export default function Message() {
  const [messages] = React.useState([
    {
      id: "001",
      title: "message001",
      content: "锄禾日当午",
    },
    {
      id: "002",
      title: "message002",
      content: "汗滴禾下土",
    },
    {
      id: "003",
      title: "message003",
      content: "谁知盘中餐",
    },
    {
      id: "004",
      title: "message004",
      content: "粒粒皆辛苦",
    },
  ]);
  return (
    <div
      className="tab-pane fade show active"
      id="message"
      role="tabpanel"
      aria-labelledby="message-tab"
    >
      <ul className="news-list">
        {messages.map((item) => {
          return (
            <li key={item.id}>
              <Link to="detail">{item.title}</Link>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定路由组件的展示位置 */}
      <Outlet />
    </div>
  );
}

路由配置完成,页面效果如下:

2. 通过 params 参数传参

传参一共分为三步:路由表添加参数占位、跳转路由传参、以及参数的接收。

  • 配置路由(添加占位符)

在路由路径中使用 : 来指定参数名。

javascript 复制代码
  {
    path: "message",
    element: <Message />,
    children: [
      {
        path: "detail/:id/:title/:content",
        element: <Detail />
      }
    ]
  },
  • 传递参数(跳转路由)

在组件中把参数直接拼接到 URL 路径中。

javascript 复制代码
<Link to={`detail/${item.id}/${item.title}/${item.content}`}>{item.title}</Link>
  • 接收参数

在目标组件中通常使用 useParams 钩子获取路由传过来的参数对象。

src/pages/Home/Message/Detail/index.jsx

javascript 复制代码
import { useParams } from "react-router-dom";

export default function Detail() {
  const { id, title, content } = useParams();
  return (
    <div>
      <p>id:{id}</p>
      <p>title:{title}</p>
      <p>content:{content}</p>
    </div>
  );
}

那么现在参数已经成功的传给了 Detail 组件:

注意:除了使用useParams获取传过来的 params 参数,使用 useMatch也是可以的(通常不会使用,了解即可)。

javascript 复制代码
import { useMatch } from "react-router-dom";

const matchObj = useMatch('/home/message/detail/:id/:title/content');

search 路由传参通过在 URL 后拼接 ?key=value 的形式传递数据,并在目标组件中使用 useSearchParams 钩子来获取参数。

  • 传递 search 参数

通过组件里的 <Link> 标签传递 search 参数。

javascript 复制代码
{/* 直接拼接字符串路径 */}
<Link to={`detail?id=${item.id}&title=${item.title}&content=${item.content}`}>{item.title}</Link>
  • 接收 search 参数

在目标组件中,导入并使用 useSearchParams 钩子。它的用法类似于 React 的 useState(你要通过一个数组 去接受),它会返回一个包含 search参数更新search参数的函数 的数组。

src/pages/Home/Message/Detail/index.jsx

javascript 复制代码
import { useSearchParams } from "react-router-dom";

export default function Detail() {
  const [search, setSearch] = useSearchParams();
  // 获取具体参数值
  const id = search.get("id");
  const title = search.get("title");
  const content = search.get("content");

  return (
    <div>
      <p>id:{id}</p>
      <p>title:{title}</p>
      <p>content:{content}</p>
    </div>
  );
}

页面效果:

  • setSearch 函数

它主要用于更新你所收到的search参数。

比如:我们在 Detail 组件中添加一个更新按钮,点击可以更新search参数。

javascript 复制代码
import { useSearchParams } from "react-router-dom";

export default function Detail() {
  const [search, setSearch] = useSearchParams();
  const id = search.get("id");
  const title = search.get("title");
  const content = search.get("content");

  return (
    <div>
      <button
        onClick={() => setSearch("id=000&title=message000&content=lalaland")}
      >
        改变search的值
      </button>
      <p>id:{id}</p>
      <p>title:{title}</p>
      <p>content:{content}</p>
    </div>
  );
}

点击前:

点击后:

注意:除了使用useSearchParams获取传过来的 search 参数,使用 useLoaation也是可以的(通常不会使用,了解即可)。

javascript 复制代码
import { useLocation } from "react-router-dom";

const locationObj = useLocation();

4. 通过 state 参数传参

使用 state 传参是通过路由导航时附加一个隐藏的状态对象,不会在 URL 地址栏中显示,非常适合传递敏感信息或复杂数据。

  • 绑定 state 属性传递参数

直接在 <Link> 上绑定 state 属性。

javascript 复制代码
<li key={item.id}>
  <Link
    to="detail"
    state={{
      id: item.id,
      title: item.title,
      content: item.content,
    }}
  >
    {item.title}
  </Link>
</li>
  • 接收参数

在目标组件中,使用 useLocation 钩子函数获取 location.state 中的数据。

javascript 复制代码
import { useLocation } from "react-router-dom";
const loacationObj = useLocation();

获取到的loacationObj如下所示,只要对它解构便可以拿到对应的参数值。

src/pages/Home/Message/Detail/index.jsx

javascript 复制代码
import { useLocation } from "react-router-dom";

export default function Detail() {
  const { state: { id, title, content } } = useLocation();

  return (
    <div>
      <p>id:{id}</p>
      <p>title:{title}</p>
      <p>content:{content}</p>
    </div>
  );
}

页面效果:


八、编程式路由导航

React Router v6 使用 useNavigate 钩子(Hook)来实现函数组件中的编程式路由跳转。它替代了旧版本中的 useHistory

两者都是实现路由跳转的方式,但本质完全不同:

  • Navigate 是 React 组件:必须在组件的渲染输出中使用,通过渲染这个组件触发跳转,属于声明式导航。
  • useNavigate 是 React 钩子:调用它会返回一个跳转函数,通过主动调用函数触发跳转,属于编程式导航。

2. 基本用法

在函数组件中调用 useNavigate 得到一个导航函数,然后调用它来跳转页面。

javascript 复制代码
import { useNavigate } from 'react-router-dom';

const navigate = useNavigate();
navigate('xxx')

我们还是继续以上面的 Message 组件为例,我们分别在<Link>后面加上一个【跳转详情 】的按钮,点击该按钮,即可跳转到对应的 Detail组件。

src/pages/Home/Message/index.jsx

javascript 复制代码
import React from "react";
// 1. 引入 useNavigate
import { Link, Outlet, useNavigate } from "react-router-dom";

export default function Message() {
  // 2. 获取navigate函数用于跳转
  const navigate = useNavigate();
  const [messageState] = React.useState([...]);

  // 点击前往详情页面
  function toDetailPage(item) {
    // 3.实现跳转并传入state参数
    navigate('detail', {
      // 是否为replace跳转模式,默认为 false,如果为默认值时可以省略
      replace: false,
      state: {
        id: item.id,
        title: item.title,
        content: item.content
      }
    });
  }

  return (
    <div
      className="tab-pane fade show active"
      id="message"
      role="tabpanel"
      aria-labelledby="message-tab"
    >
      <ul className="news-list">
        {messageState.map((item) => {
          return (
            <li key={item.id}>
              <Link
                to="detail"
                state={{
                  id: item.id,
                  title: item.title,
                  content: item.content,
                }}
              >
                {item.title}
              </Link>
              <button onClick={() => toDetailPage(item)}>前往详情</button>
            </li>
          );
        })}
      </ul>
      <hr></hr>
      <Outlet />
    </div>
  );
}

这样就实现了我们想要的效果。

上面的案例演示了编程式路通过state传参的过程,那如果想通过paramssearch传参呢?

其实很简单,只需要把参数拼接在url的后面就可以了!

  • 通过 params 传参
javascript 复制代码
navigate(`detail/${item.id}/${item.title}/${item.content}`);
  • 通过 search 传参
javascript 复制代码
navigate(`detail?id=${item.id}&title=${item.title}&content=${item.content}`);
  • 首先,我们将标题的部分封装成一个 Header 组件。

src/components/Header/index.jsx

javascript 复制代码
import React from "react";

export default function Header() {
  return (
    <div className="row">
      <div className="col">
        <h1 className="header-title text-dark">React Router Demo</h1>
        <span className="text-muted"></span>
      </div>
    </div>
  );
}
  • 然后,在App.jsx中引入 Header 组件。

App.jsx

javascript 复制代码
import { NavLink, useRoutes } from "react-router-dom";
import routes from "./routes"
import Header from "./components/Header";

import "./App.css";

function App() {
  const element = useRoutes(routes);

  return (
    <div className="App">
      <div className="container mt-4" style={{ maxWidth: "1000px" }}>
        {/* 引入Header组件 */}
        <Header />

        <div className="row mt-4">
          <div className="col-3">
            <div className="list-group">
              {/* 路由链接 */}
              <NavLink
                to="/about"
                className="list-group-item list-group-item-action text-secondary"
              >
                About
              </NavLink>
              <NavLink
                to="/home"
                className="list-group-item list-group-item-action text-secondary"
              >
                Home
              </NavLink>
            </div>
          </div>

          <div className="col-9 content-area">
            {/* 注册路由 */}
            {element}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;
  • 在React Router v6中,不管是路由组件还是一般组件,只要想实现编程式路由导航,就直接使用 useNavigate Hook 即可。

src/components/Header/index.jsx

javascript 复制代码
import React from "react";
import { useNavigate } from "react-router-dom";
import "./index.css";

export default function Header() {
  const navigate = useNavigate();
  return (
    <div className="header row">
      <div className="col">
        <h1 className="header-title text-dark">React Router Demo</h1>
        <span className="text-muted"></span>
        <button className="btn btn-warning" onClick={() => navigate(-1)}>
          后退←
        </button>
        <button className="btn btn-success" onClick={() => navigate(1)}>
          前进→
        </button>
      </div>
    </div>
  );
}

这样前进和后退的效果就实现了。


九、useInRouterContext

useInRouterContext 用于检查当前组件是否被渲染在 Router 上下文内部,它返回一个布尔值(truefalse)。

javascript 复制代码
import { useInRouterContext } from 'react-router-dom';

function MyComponent() {
  const isInRouter = useInRouterContext();

  return (
    <div>
      {isInRouter ? <p>当前在路由环境中</p> : <p>当前不在路由环境中</p>}
    </div>
  );
}

十、useNavigationType

  • 作用:返回当前的导航类型(用户是如何来到当前页面的);
  • 返回值POPPUSHREPLACE
  • 备注POP 是指在浏览器中直接打开了这个路由组件(刷新页面);
javascript 复制代码
import { useNavigationType } from 'react-router-dom';
const actionType = useNavigationType(); // 返回 POP, PUSH 或 REPLACE

十一、useOutlet

  • 作用:用来返回当前路由层级下应当渲染的子路由内容。
javascript 复制代码
import { useOutlet } from 'react-router-dom';

const result = useoutlet()
console.log(result)
// 如果嵌套路由没有挂载,则result为null
// 如果嵌套路由已经挂载,则展示嵌套的路由对象

我们可以在上面案例的Message组件中打印一下 useOutlet 的返回值:


十二、useResolvedPath

  • 作用 :用于根据当前 url 来解析其中的 pathsearchhash 值。
javascript 复制代码
import { useResolvedPath } from "react-router-dom";

function MyComponent() {
    const path = useResolvedPath('/abc/d/e?f=1&g=2&h=3#qwer');
  	console.log(path);
  
  // path 的结构:
  // {
  //   pathname:  "/abc/d/e",
  //   search:  "?f=1&g=2&h=3",
  //   hash:  "#qwer"
  // }
}
相关推荐
少冰1 小时前
前端埋点如何落地:从事件设计到上线验收
前端·数据分析·数据可视化
toooooop81 小时前
thinkphp查询数据表最后的自增id
前端·javascript·数据库
晚安日记wanna1 小时前
简历写精通浏览器原理一问 URL 到渲染直接哑火...
前端·面试
晚安日记wanna1 小时前
四年前端被问 Code Review90人只看得懂空格和命名
前端·面试
IPdodo_1 小时前
curl 如何测试代理 IP?HTTP、SOCKS5 与认证参数示例
前端·网络·python·https·网络调试
做前端的娜娜子1 小时前
施工蓝图:vite.config.js —— 项目的指挥中心
前端·react native·vite
宸翰1 小时前
解决uni-app 中 SVG 图片在 iOS 端显示模糊问题
前端·uni-app
数据掘金1 小时前
第三方 SDK 集体故障时,你的 App 会怎样?可落地的监控与降级方案
前端
AI分享猿1 小时前
UI设计Prompt系列(十七):团队如何管理UI Prompt——减少产品、设计与开发之间的信息损耗
前端·prompt