React Router v6 完全指南(下)

简介: React Router v6 完全指南(下)

React Router v6 完全指南(上)https://developer.aliyun.com/article/1411423


(5)路由顺序

在 React Router v6 以前,我们必须按照一定的顺序来定义路由,以获得准确的渲染。在 v6 及之后的版本中,路由定义的顺序无关紧要。

以下代码在 v5 中,/product/new将匹配到第一个路由并渲染 Product 组件:

<Switch>  <Routepath="/product/:id"component={Product} />  <Routepath="/product/new"component={NewProduct} />Switch>

而在 v6 中,将 组件替换为了 组件。/products/new将匹配这两个路由,但只会渲染NewProduct组件,因为它是更具体的匹配:

<Routes>  <Routepath="/product/:id"element={<Product />} />  <Routepath="/product/new"element={<NewProduct />} />Routes>

3. 编程式导航

React Router 提供了两种不同的编程式导航方式:

  • 声明式导航组件 组件
  • 命令式导航方法useNavigate Hook

我们可以使用这两种编程的方式来跳转到指定的路由,也可以实现路由的重定向,比如在浏览器的地址栏输入一个 URL 并进行跳转时,如果应用中没有定义该路由,就跳转到应用的首页。

(1)Navigate

组件是一种声明式的导航方式。使用 Navigate 组件时,首先需要从 react-router-dom 导入 Navigate 组件。然后在 Navigate 组件中通过 to props 来指定要跳转的路径:

import { NavLink, Routes, Route, Navigate } from "react-router-dom";
import Product from "./Product";
import About from "./About";
import Home from "./Home";
import "./styles.css";
export default function App() {
  return (
    <div className="App">
      <header>
        <h1>Hello World</h1>
      </header>
      <nav>
        <NavLink to="">首页</NavLink>
        <NavLink to="product">产品</NavLink>
        <NavLink to="about">关于</NavLink>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="product" element={<Product />} />
        <Route path="about" element={<About />} />
        <Route path="*" element={<Navigate to="/" />} />
      </Routes>
    </div>
  );
}

这样,当在浏览器地址栏输入一个未定义的路由时,就会要转到首页。

(2)useNavigate

useNavigate Hook是一种命令式导航方式。使用这个 Hook 时,首先需要从 react-router-dom 中导入 useNavigate,然后传递给它需要跳转的路由即可。假如在提交完表单之后,跳转到主页,可以这样实现:

import { useNavigate } from 'react-router-dom
function Register () {
  const navigate = useNavigate()
  return (
    <div>
      <Form afterSubmit={() => navigate('/')} />
    </div>
  )
}

4. 通过路由传递状态

在 react-router-dom 中可以通过以下三种方式来传递状态:

  • 使用 Link 组件
  • 使用 Navigate 组件
  • 使用 useNavigate 钩子

(1)Link

下面来使用 Link 组件通过 state props 来将数据从产品页面传递到主页:

import React from "react";
import { Link } from "react-router-dom";
function Contact() {
  return (
    <div>
      <header>产品页面</header>
      <Link to="/" state={"From Product"}>
        返回
      </Link>
    </div>
  );
}
export default Contact;

现在我们就将需要的数据传递出来了,那该如何在首页获取从产品页面传递出来的数据呢?可以在接收信息的页面(首页)中使用一个名为 useLocation 的钩子来获取数据:

import { useLocation } from "react-router-dom";
import "./styles.css";
export default function Settings() {
  let location = useLocation();
  return (
    <div className="App">
      <header>首页</header>
      <p>{location.state}</p>
    </div>
  );
}

当在产品页面点击返回时,就会跳转到首页,并且首页显示了从产品页面传递出来的数据:

21.webp.jpg

(2)Navigate

Navigate 组件也可以在 react-router-dom 中传递状态,其使用方式和 Link 组件类似。假如当点击关于按钮时,跳转到首页,并告诉首页该跳转是从哪个页面来的:

javascript

复制代码

<Route path="/about" element={<Navigateto="/"state={"FromAbout"} />} />

在首页中仍然是使用 useLocation 钩子来获取状态值:

import { useLocation } from "react-router-dom";
import "./styles.css";
export default function Settings() {
  let location = useLocation();
  return (
    <div className="App">
      <header>首页</header>
      <p>{location.state}</p>
    </div>
  );
}

当在首页点击“关于”时,就会重定向到首页,并且首页中会显示 From About:

22.webp.jpg

(3)useNavigate

上面我们介绍了如何使用 useNavigate 钩子来进行重定向,在调用 navigate() 函数时,给它传递了一个参数,即要重定向的路径。实际上,navigate() 函数接受两个参数,第一个参数就是跳转的路径,第二个参数是包含状态的对象。可以借助  useNavigate Hook 来实现状态传递:

import { useNavigate } from 'react-router-dom
function Register () {
  const navigate = useNavigate()
  return (
    <div>
      <Form afterSubmit={() => navigate('/', { state: "From the About Page"})} />
    </div>
  )
}


在首页中仍然是使用 useLocation 钩子来获取状态值,和上面两种方式一样,这里不再多介绍。

5. 动态路由

一个很常见的场景,在维基百科进行搜索时,URL 的模式始终是一样的,如下:

javascript

复制代码

https://zh.wikipedia.org/wiki/{keyword}

这里的 keyword 就是我们在维基百科中搜索的内容,这个内容是不固定的,并且有很多很多,我们不可能为每个关键词都创建一个路由。其实,只需要声明一个带有 keyword 占位符的路由即可。对于上面的例子,只需要将 Route 组件的 path props 声明为这样:

javascript

复制代码

<Route path="/wiki/:keyword" element={<Wiki />} />

这时,无论是访问/wiki/javascript还是/wiki/react,都会加载 Wiki 组件。

那我们该如何在组件中访问 URL 中的动态部分呢?从 v5.1 开始,React Router 就提供了一个 useParams Hook,它返回一个对象,该对象具有 URL 参数及其值之间的映射。使用方式如下:

import React from 'react'
import {useParams} from 'react-router';
function Wiki() {
  const { keyword } = useParams()
  return (
    <div>{ keyword }</div>
  )
}

这样,通过获取到的 URL 参数,就可以请求页面对应的数据。

6. 嵌套路由

嵌套路由允许父路由充当包装器并控制子路由的渲染。比如,在应用中点击消息时,会跳转到 /messages 路由,并显示所有的通知列表。当点击某一条消息时,就会跳转到 /messages/:id 路由,这时就能看到指定 id 的消息详情,同时消息列表是显示在左侧的。这个场景就要依赖嵌套路由来实现。下面来看看如何使用 React Router 实现这种嵌套路由模式。

从最基础的结构开始定义:

import React from 'react'
import {useParams} from 'react-router';
function Wiki() {
  const { keyword } = useParams()
  return (
    <div>{ keyword }</div>
  )
}

现在,我们希望 Messages 能够控制渲染子路由,那能不能直接在 Messages 组件中来定义子路由呢?就像这样:

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages" element={<Messages />} />
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}

现在,当用户导航到 /messages 时, React Router 会呈现 Messages 组件。Messages 组件中通过 Conversations 组件来显示消息列表,然后使用将 /messages/:id 映射到 Chat 组件的 Route 来渲染另一个 Routes

注意: 这里不必在嵌套路由中包含完整的 /messages/:id 路径,因为 Routes 是很智能的,当省略了前导 /,就会认为这条路径是相对于父级 /messages 的。

这样,只有在跳转到/Messages时才会渲染消息列表。当访问与 /messages/:id 模式匹配的路由时,消息列表就消失了,嵌套路由永远不被会渲染。

为了解决这个问题,我们需要告诉 React Router 想要在路由为 /messages 时或者为任何其他匹配 /messages/* 模式的路由时要渲染消息列表。

那如果只是将路径修改为 /messages/*会怎样呢?

function Messages() {
  return (
    <div>
      <Conversations />
      <Routes>
        <Route path=":id" element={<MessagesDetails />} />
      </Routes>
    </div>
  );
}

这样做的确生效了,无论是导航到 /messages 还是 /messages/:id,都能正常加载消息列表组件:

77.webp.jpg

通过将 /* 附加到 /messages 路径的末尾,实际上是在告诉 React Router,Messages 有一个嵌套的 Routes 组件。并且父路径应该匹配 /messages 以及与 /messages/* 匹配的任何其他路由。

当我们希望在子 Route 控制渲染嵌套路由时,这是有效的。但是如果我们希望在 App 组件包含创建嵌套路由所需的所有信息,而不是必须在 Messages 组件中定义呢?React Router 也是支持这种创建嵌套路由的方式:

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages" element={<Messages />}>
        <Route path=":id" element={<MessagesDetails />} />
      </Route>
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}

这里以声明式的方式将子 Route 嵌套为父 Route 的子级。和上面一样,子路由是相对于父路由的,因此不需要包含父 (/messages) 路径。

现在,只需要告诉 React Router 应该在父路由(Messges)中的哪个位置渲染子路由(MessagesDetails)。这就就需要使用 React Router 的 Outlet 组件:

import { Outlet } from "react-router-dom";
function Messages() {
  return (
    <div>
      <Conversations />
      <Outlet />
    </div>
  );
}

如果应用的路由与嵌套 Route 的路径匹配,Outlet 组件就会渲染 Route 的元素。 根据上面的 Routes,如果在当前的路由是 /messagesOutlet 组件将渲染为 null;如果当前的路由是 /messages/1Outlet 组件将渲染 组件。

7.  查询参数

在 React Router 中,如何从 URL 中获取参数呢?例如以下 URL:

javascript

复制代码

twitter.com/search?q=react&src=typed_query&f=live

从 v6 开始,React Router  使用 URLSearchParams API 来处理查询字符串,URLSearchParams 内置于所有浏览器(IE 除外)中,并提供了处理查询字符串的实用方法。当创建 URLSearchParams 实例时,需要向它传递一个查询字符串:

const queryString = "?q=react&src=typed_query&f=live";
const sp = new URLSearchParams(queryString);
sp.has("q"); // true
sp.get("q"); // react
sp.getAll("src"); // ["typed_query"]
sp.get("nope"); // null
sp.append("sort", "ascending");
sp.toString(); // "?q=react&src=typed_query&f=live&sort=ascending"
sp.set("q", "bytes.dev");
sp.toString(); // "?q=bytes.dev&src=typed_query&f=live&sort=ascending"
sp.delete("sort");
sp.toString(); // "?q=bytes.dev&src=typed_query&f=live"

React Router 提供了一个自定义的 useSearchParams Hook,它是基于 URLSearchParams 进行的封装。useSearchParams 返回一个数组,该数组第一个元素是 URLSearchParams 的实例,第二个元素是更新查询参数的一个方法。

对于上面的 URL,使用 useSearchParams 从查询字符串中获取值:

import { useSearchParams } from 'react-router-dom'
const Results = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const q = searchParams.get('q')
  const src = searchParams.get('src')
  const f = searchParams.get('f')
  return (
    // ...
  )
}

如果需要更新查询字符串,可以使用 setSearchParams,向它传递一个对象,该对象的key/value 对将作为 &key=value 添加到 url:

const Results = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const q = searchParams.get('q')
  const src = searchParams.get('src')
  const f = searchParams.get('f')
  const updateOrder = (sort) => {
    setSearchParams({ sort })
  }
  return (
    ...
  )
}

8. Route 配置

React Router v6 内置了一个 useRoutes Hook,它在功能上等同于 ,但它是使用 JavaScript 对象而不是 元素来定义路由。这个对象具有与普通 元素相同的属性,但它们不需要使用 JSX 来编写。

useRoutes 的返回值要么是一个有效的 React 元素(可以使用它来渲染路由树),如果没有匹配项,则返回 null

假如应用中有以下路径:

/
/invoices
  :id
  pending
  complete

使用 组件来定义路由将会是这样的:

export default function App() {
  return (
    <div>
      <Navbar />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/invoices" element={<Invoices />}>
          <Route path=":id" element={<Invoice />} />
          <Route path="pending" element={<Pending />} />
          <Route path="complete" element={<Complete />} />
        </Route>
      </Routes>
    </div>
  );
}

useRoutes 是利用 JavaScript 对象完成的,而不是使用 React 元素 (JSX) 来声明路由。定义形式如下:

import { useRoutes } from "react-router-dom";
const routes = useRoutes([
  { path: "/", element: <Home /> },
  {
    path: "/invoices",
    element: <Invoices />,
    children: [
      { path: ":id", element: <Invoice /> },
      { path: "/pending", element: <Pending /> },
      { path: "/complete", element: <Complete /> },
    ],
  },
]);
export default function App() {
  return (
    <div>
      <Navbar />
      {routes}
    </div>
  );
}
相关文章
|
15天前
|
前端开发
【掰开揉碎】React Router——React应用导航(二)
【掰开揉碎】React Router——React应用导航(二)
|
2月前
|
前端开发 JavaScript API
介绍一下React Router的工作原理。
介绍一下React Router的工作原理。
31 5
|
3月前
|
存储 资源调度 前端开发
React Router v6 完全指南(上)
React Router v6 完全指南(上)
152 0
|
5月前
|
存储 前端开发 JavaScript
React Router,常用API有哪些?
React Router,常用API有哪些?
43 0
|
8月前
|
前端开发
【react 中router v6 与 v5 区别】
【react 中router v6 与 v5 区别】
|
10月前
|
前端开发
React全家桶建站教程-Router #6
React全家桶建站教程-Router #6
52 0
|
2月前
|
开发框架 前端开发 JavaScript
探索前端开发中的跨平台框架React Native
本文将介绍前端开发中一种备受关注的跨平台框架React Native,通过比较原生应用与React Native的优缺点,探讨其在实际项目中的应用以及未来发展趋势。
|
2月前
|
开发框架 前端开发 JavaScript
从零开始学习React Native开发
React Native是一种基于React框架的移动端开发框架,使用它可以快速地构建出高性能、原生的移动应用。本文将从零开始,介绍React Native的基础知识和开发流程,帮助读者快速入门React Native开发,并实现一个简单的ToDo应用程序。
|
3月前
|
前端开发 JavaScript Android开发
跨端技术栈综合考察:深入剖析 UniApp、Flutter、Taro 和 React Native 的优势与限制
跨端技术栈综合考察:深入剖析 UniApp、Flutter、Taro 和 React Native 的优势与限制
|
1天前
|
开发框架 前端开发 JavaScript
【专栏】Flutter vs React Native:跨平台移动应用开发的比较
【4月更文挑战第27天】本文对比分析了Flutter和React Native两大跨平台移动开发框架。Flutter,由Google推出,以其接近原生的性能、快速启动和流畅滚动受青睐,适合高性能和高度定制的项目。React Native,Facebook维护,依赖JavaScript,虽性能受限,但热重载优势和丰富第三方库使其适合快速迭代的项目。两者都在拓展多平台应用,Flutter在桌面和Web,React Native在Windows。选择框架需考虑项目需求、团队技能和性能效率平衡。