React Router 完美教程(下)

我们书接上回,继续我们的React Router 路由之路:
我们到目前为止都没有用到 state、useEffect、redux等状态管理器。但也达到了我们的设计目的。

注意,action 返回的结果 可以在组件中使用 useActionData() 来获取。就像 useLoaderData() 的使用一样。

loader 中的 URL参数

接着上回的示例文件, 新建几个无名联系人。虽然联系人信息没有变化,但我们仔细查看地址栏,会发现 ID 是会发生变化的。因为我们在createContact()中为每个联系人分配了一个ID号。

我们再次查看路由信息,

...
{path: "contacts/:contactId",element: <Contact />,
},...

在上节中我们已经详细的说过,通过 :contactId 路由变量,我们可以获取到这个符在地址中的 id 号的。我们再次对 utils.jsx 进行修改:注意里面的备注信息,很重要。

import { getContacts, createContact, getContact } from "./contacts";// 模拟网络请求,获取数据
export async function rootLoader() {const contacts = await getContacts();return { contacts };
}//创建新的联系人
export async function rootAction() {const contact = await createContact();return { contact };
}// 根据 URL 中的ID 获取对应的联系人信息。变量 params 是一个对象,包含了 URL 中的参数。
// 其中 contactId 就是路由变量中的 :contactId,即 /contacts/:contactId。同名变量。
export async function contactLoader({ params }) {const contact = await getContact(params.contactId);return { contact };
}

为了不造成理解上的概念混淆,我们把原先的action 更名为 rootAction, 新增了 contactLoader函数,你应该理解过来了,这个loader是用有 组件的路由配置中的。 然后在 routerConfig.jsx中进行修改。

import { createBrowserRouter } from "react-router-dom";import Root from "./Root";
import Error404 from "./Error404";
import Contact from "./Contact";
import {rootLoader,rootAction,contactLoader,
} from "./utils";const router = createBrowserRouter([{path: "/",element: <Root />,errorElement: <Error404 />,loader: rootLoader,action: rootAction,children: [{path: "contacts/:contactId",element: <Contact />,loader: contactLoader},]}
]);export default router;

现在组件<Contact/> 加载后就能获取到相应的 contact 信息了,我们只要在组件内用 useLoaderData() 就能获取到。对Contact.jsx进行修改:

// Contact.jsximport {Form,useLoaderData,
} from "react-router-dom";
import Favorite from "./Favorite";export default function Contact() {const { contact } = useLoaderData();// const contact = {//     first: "Your",//     last: "Name",//     avatar: "https://placekitten.com/g/200/200",//     twitter: "your_handle",//     notes: "Some notes",//     favorite: true,// };...}

将之前表态定义的联系人信息删掉,用 上面的替换。 上面我已经注释了。

更新联系人信息

就像创建数据一样,您可以使用 更新数据。创建 EditContact.jsx 文件。

// Edit.jsximport { Form, useLoaderData } from "react-router-dom";export default function EditContact() {const { contact } = useLoaderData();return (<Form method="post" id="contact-form"><p><span>Name</span><inputplaceholder="First"aria-label="First name"type="text"name="first"defaultValue={contact.first}/><inputplaceholder="Last"aria-label="Last name"type="text"name="last"defaultValue={contact.last}/></p><label><span>Twitter</span><inputtype="text"name="twitter"placeholder="@jack"defaultValue={contact.twitter}/></label><label><span>Avatar URL</span><inputplaceholder="https://reactrouter.com/_docs/tutorial/12.webp"aria-label="Avatar URL"type="text"name="avatar"defaultValue={contact.avatar}/></label><label><span>Notes</span><textareaname="notes"defaultValue={contact.notes}rows={6}/></label><p><button type="submit">Save</button><button type="button">Cancel</button></p></Form>);
}

增加路由信息,将编辑组件添加到路由中

// routerConfig.jsximport { createBrowserRouter } from "react-router-dom";import Root from "./Root";
import Error404 from "./Error404";
import Contact from "./Contact";
import EditContact from "./EditContact";
import {rootLoader,rootAction,contactLoader,
} from "./utils";const router = createBrowserRouter([{path: "/",element: <Root />,errorElement: <Error404 />,loader: rootLoader,action: rootAction,children: [{path: "contacts/:contactId",element: <Contact />,loader: contactLoader},{path: "contacts/:contactId/edit",element: <EditContact />,loader: contactLoader,},]}
]);export default router;

配置在子路由中的目的就是在 Root 中的 <Outlet/>的位置中显示的。
注意路由的配置,当我们点击 Edit 按钮后,注意查看 URL 的变化,如:http://localhost:5173/contacts/uiahrwo/edit, 一眼就能看出可以匹配 ath: "contacts/:contactId/edit"的配置信息。
现在在界面应该如下所示:
在这里插入图片描述

现在我人需要将编辑的信息反映到 action 中来更新信息。在 utils.jsx 中创建 editAction(), 并配置到路由中。文件中的参数我已经作了详细的说明。

// utils.jsximport { redirect } from "react-router-dom";
import { getContacts, createContact, getContact, updateContact } from "./contacts";...// 更新联系人信息
// request 是一个对象,包含了请求的所有信息,包括请求头、请求体等。
// params 是一个对象,包含了 URL 中的参数,即路由变量。
// 通过 request.formData() 可以获取到表单数据,返回一个 FormData 对象。
// FormData 对象是一个键值对集合,每个键对应一个值,值可以是字符串,也可以是 Blob 对象。
// 通过 Object.fromEntries() 可以将 FormData 对象转换为一个普通的对象。
// 通过 updateContact() 更新联系人信息。
// redirect() 可以重定向到指定的 URL。
export async function editAction({ request, params }) {const formData = await request.formData();const updates = Object.fromEntries(formData);await updateContact(params.contactId, updates);return redirect(`/contacts/${params.contactId}`);
}

更新路由配置信息:

// routerConfig.jsximport { createBrowserRouter } from "react-router-dom";import Root from "./Root";
import Error404 from "./Error404";
import Contact from "./Contact";
import EditContact from "./EditContact";
import {rootLoader,rootAction,contactLoader,editAction,
} from "./utils";const router = createBrowserRouter([{path: "/",element: <Root />,errorElement: <Error404 />,loader: rootLoader,action: rootAction,children: [{path: "contacts/:contactId",element: <Contact />,loader: contactLoader},{path: "contacts/:contactId/edit",element: <EditContact />,loader: contactLoader,action: editAction},]}
]);export default router;

增加活动链接状态

现在我们有一堆记录,不清楚我们在侧边栏中查看的是哪一条。我们可以使用 NavLink 来解决这个问题。
Root 中的 Link 替换为 NavLink ,如下所示:

import {Outlet,Link,useLoaderData,Form,NavLink,
} from 'react-router-dom';export default function Root() {const { contacts } = useLoaderData();return (<><div id="sidebar"><h1>React Router Contacts</h1><div><form id="search-form" role="search"><inputid="q"aria-label="Search contacts"placeholder="Search"type="search"name="q"/><divid="search-spinner"aria-hiddenhidden={true}/><divclassName="sr-only"aria-live="polite"></div></form><Form method="post"><button type="submit">New</button></Form></div><nav>{contacts.length ? (<ul>{contacts.map((contact) => (<li key={contact.id}><NavLinkto={`contacts/${contact.id}`}className={({ isActive, isPending }) =>isActive? "active": isPending? "pending": ""}>{contact.first || contact.last ? (<>{contact.first} {contact.last}</>) : (<i>No Name</i>)}{" "}{contact.favorite && <span></span>}</NavLink></li>))}</ul>) : (<p><i>No contacts</i></p>)}</nav></div><div id="detail"> <Outlet /> </div></>);
}

请注意,我们将一个函数传递给 className 。当用户位于 NavLink 中的 URL 时,isActive 将为 true 。当它即将处于活动状态(数据仍在加载)时,isPending将为 true 。这使我们能够轻松指示用户的位置,并就已点击但仍在等待数据加载的链接提供即时反馈。

有时候我们希望数据在加载的时候有个状态反馈,如下图所示:
在这里插入图片描述

路由的加载状态可以通过 useNavigation 来获取。我们再次对 Root.jsx 进修修改:

import {// 其它代码// ... ...useNavigation,
} from "react-router-dom";// ...export default function Root() {const { contacts } = useLoaderData();const navigation = useNavigation();return (<><div id="sidebar">{/* ... */}</div><divid="detail"className={navigation.state === "loading" ? "loading" : ""}><Outlet /></div></>);
}

由于我们在本地缓存了数据,所以加载速度很快,看不到这个效果,但如果是从网络上执行耗时的操作时,这种效果就很明显了。

删除联系人

现在我们再来完善我们的应用,当我们点击删除按钮后,我们要把 联系人的 ID 传递给路由,再由相应的action去完成删除工作。

查看 Contact.jsx 文件

<Formmethod="post"action="destroy"onSubmit={(event) => {if (!confirm("Please confirm you want to delete this record.")) {event.preventDefault();}}}
><button type="submit">Delete</button>
</Form>

仔细查看<Form>中的参数, action 提交到 destroy 路由,这个路由地址是相对地址。由于Contact 的路由是 contacts/:contactId, 所以这个destroy的路径为 contacts/:contactId/destroy,
我们先创建这个路由的action函数: deleteAction(),

// utils.jsximport { redirect } from "react-router-dom";
import {getContacts,createContact,getContact,updateContact,deleteContact
} from "./contacts";...// 删除联系人
export async function destroyAction({ params }) {await deleteContact(params.contactId);return redirect("/");
}

并将它配置到路由中

 ... import { action as destroyAction } from "./routes/destroy";const router = createBrowserRouter([{path: "/",...children: [...{path: "contacts/:contactId/destroy",action: destroyAction,},],},
]);...

由于我们没有在子路由中配置 errorElement ,这是因为我们在destroyAction中直接重定向到 " / ", 所以这个element就没有创建的必要了。事实上也的确没有这个需求

 // routerConfig.jsx[...{path: "contacts/:contactId/destroy",action: destroyAction,errorElement: <div>Oops! There was an error.</div>,},
];

还有一点非常重要,Form 的 提交方法为 post 时才能激活路由中的action操作。

索引路由

每当我们加载这个App时,你会发现右侧是一个空页面。就像下面这样:
在这里插入图片描述

当一条路由有子路由时,而当前页面又处在父路由层级时,<Outlet>没有任何子路由与之匹配,所以就没有可渲染的界面。这个时候可以将索引路由视为填充该空间的默认子路由。
创建 Index 组件如下所示

// Index.jsxexport default function Index() {return (<p id="zero-state">This is a demo for React Router.<br />Check out{" "}<a href="https://reactrouter.com">the docs at reactrouter.com</a>.</p>);
}

配置这个Index到路由

// routerConfig.jsx...import Index from "./Index";...const router = createBrowserRouter([{path: "/",element: <Root />,errorElement: <Error404 />,loader: rootLoader,action: rootAction,children: [{ index: true, element: <Index /> },...]}
]);export default router;

这个时候我们进入App重新渲染后界面如下:
在这里插入图片描述

完美。
现在还有最后一个功能没有完成,就是搜索功能,我们希望根据输入的关键词来搜索出联系人。

URL的搜索参数与Get提交

传统的html表单中,如果没有指定提交方法的话默认为get方式提交到服务器,如我们Root.jsx中搜索框部分,这自然不是我们想要的结果,我们只是想把输入参数反应到地址栏中而不影响浏览器的变化,正好,Form 可以做到。我们把Root中的 html 元素 form 改成 React Router 中的 Form组件就好了,就像下面这样:

// Root.jsx
...
<Form id="search-form" role="search"><inputid="q"aria-label="Search contacts"placeholder="Search"type="search"name="q"/><divid="search-spinner"aria-hiddenhidden={true}/><divclassName="sr-only"aria-live="polite"></div>
</Form>
...

现在你的搜索栏中输入些内容回车后,你会发现浏览器的地址栏信息也会发会变化,但浏览器并没有网络请求操作。这正是我们目的,
现在我们修改 rootLoader()函数, 以获取get参数,并根据这个参数做出筛选联系人的反应:

// utils.jsx...// 模拟网络请求,获取数据
export async function rootLoader({request}) {const url = new URL(request.url);const q = url.searchParams.get("q");const contacts = await getContacts(q);return { contacts };
}
...

现在就很美了。
在这里插入图片描述

再次强调:因为这是一个 get 的,而不是 postReact Router 路由器不调用 action 。提交 GET 表格与单击链接相同:只有URL更改。这就是为什么我们添加的过滤代码在loader中,而不是在 action 中。

这也意味着这是一个普通的页面导航。您可以单击“后退”按钮以返回到自己的位置。

最后

关于路由的主要应用技术我已经讲完了,根据这些应用方法,配置 React的状态管理,会使我们的应用更加灵活,设计更加方便。只要多练习,多思考,就一定能开发出非常完美的产品。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/659858.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux上使用OpenCvSharp

前言 OpenCV是一个基于Apache2.0许可(开源)发行的跨平台计算机视觉和机器学习软件库,它具有C++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS。OpenCvSharp是一个OpenCV的 .Net wrapper,应用最新的OpenCV库开发,使用习惯比EmguCV更接近原始的OpenC…

STL - string类

1、标准库中的string类 1.1、string类了解 字符串是表示字符序列的类标准的字符串类提供了对此类对象的支持&#xff0c;其接口类似于标准字符容器的接口&#xff0c;但添加了专门用于操作单字节字符字符串的设计特性string类是使用char (即作为它的字符类型&#xff0c;使用…

MD5算法:高效安全的数据完整性保障

摘要&#xff1a;在数字世界中&#xff0c;确保数据完整性和安全性至关重要。消息摘要算法就是一种用于实现这一目标的常用技术。其中&#xff0c;Message Digest Algorithm 5&#xff08;MD5&#xff09;算法因其高效性和安全性而受到广泛关注。本文将详细介绍MD5算法的优缺点…

web应用课——(第四讲:中期项目——拳皇)

代码AC Git地址&#xff1a;拳皇——AC Git链接

Maven:一些常见问题

1、ReasonPhrase: Forbidden a.注意用户的权限以及角色role的设置&#xff0c;一般是没有权限才会被禁止的。 2、Failed to collect dependencies a.需要把parent工程&#xff0c;也就是package是pom的那个工程先install一下&#xff0c;或者deploy b.需要注意在设置的工厂里面…

87 SpringMVC 上传文件在业务代码中拿不到文件数据

前言 呵呵 最近在整理文件上传部分的东西的时候, 发现了一个问题 文件上传部分 有一些基础的问题, 可以参见 29 SpringMVC 上传文件未生成临时文件, 我们这里上传的文件的大小是 大于 sizeThreshold 的 SpringMVC 上传文件的时候会生成一个临时文件, 我想直接使用这个临时…

【开源】SpringBoot框架开发海南旅游景点推荐系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

【DB2】—— 一次关于db2 sqlcode -420 22018的记录

情况描述 在DB2 10.5数据库中执行以下SQL语句&#xff1a; SELECT * FROM aa WHERE aa.ivc_typ IN (213,123,12334,345)其中aa.ivc_typ列的类型为VARCHAR(10) 关于执行会发生以下情况 类型转换&#xff1a;SQL引擎会尝试把IN列表中的整数常量转换为VARCHAR(10)类型&#xf…

【从浅到深的算法技巧】优先队列

5.6 优先队列 许多应用程序都需要处理有序的元素&#xff0c;但不一定要求它们全部有序&#xff0c; 或是不一定要一次就将它们排序。很多情况下我们会收集一些元素&#xff0c; 处理当前键值最大的元素&#xff0c;然后再收集更多的元素&#xff0c;再处理当前键值最大的元素&…

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏5(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言修改鼠标光标和中心提示图鼠标光标素材修改默认鼠标光标修改中心提示图 拾取提示弹窗简单绘制UI拾取弹窗功能 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使…

canvas的一些基础

在 Canvas 中&#xff0c;基本图形有两种&#xff1a;直线图形和曲线图形 直线图形&#xff1a;直线、矩形(描边矩形和填充矩形)、多边形 曲线图形&#xff1a;曲线和弧线&#xff08;弧线是圆的一部分&#xff0c;曲线则不一定&#xff0c;弧线上的每个点都具有相同的曲率&…

3D Gaussian Splatting-实时辐射场渲染技术

引用自&#xff1a;https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/3d_gaussian_splatting_high.pdf 概述&#xff1a; 该论文介绍了一种用于实时辐射场渲染的3D高斯点渲染技术。 其基本原理是&#xff1a; 一&#xff1a;首先从SfM校准的图像及其对应的稀疏点云…

十分钟快速上手Spring Boot与微信小程序API接口的调用,快速开发小程序后端服务

1.1 微信小程序API接口介绍 微信小程序API接口是连接小程序前端与后端服务器的桥梁&#xff0c;它提供了丰富的功能接口&#xff0c;包括用户信息、支付、模板消息、数据存储等。这些API接口能够满足开发者在小程序中实现各种复杂业务逻辑的需求。 用户信息接口 用户信息接口…

计算机服务器中了locked勒索病毒怎么办,locked勒索病毒解密流程

随着网络技术在企业生产生活中的应用&#xff0c;越来越多的企业开始走向数字化办公模式&#xff0c;极大地提升了企业办公与生产效率&#xff0c;而其中的企业数据起到了关键性作用&#xff0c;企业的数据安全是众多企业关心的话题。但网络是一把双刃剑&#xff0c;近期&#…

【C++】开源:Windows图形库EasyX配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Windows图形库EasyX配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#…

【 USRP 相控阵】ADAR1000 : 4 通道 X 频段和 Ku 频段波束形成器,8 GHz 至 16 GHz

介绍 ADAR1000 是一款适用于相控阵的 4 通道 X 和 Ku 频段波束形成内核芯片。此器件在接收和发射模式之间以半双工状态工作。在接收模式下&#xff0c;输入信号通过四个接收通道后在公共 RF_IO 引脚上组合在一起。在发射模式下&#xff0c;RF_IO 输入信号拆分后通过四个发射通…

Linux 指令 第二弹

1 查看文件系统空间 1.1 查看文件系统磁盘使用情况 df df -h 显示所有挂载的文件系统的信息&#xff0c;包括使用情况和可用空间。 -h 的意思是以人类可读方式显示 1.2 查看某个文件夹的磁盘使用 du du -h /path/to/yourdirectory1.3 ls ls -lh /path/to/yourdirectory显示…

智慧食堂预点餐管理系统-计算机毕业设计源码48846

摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;餐饮业当然也不例外。智慧食堂预点餐管理系统小程序是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&…

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于OOA-LSSVM鱼鹰算法优化最小支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于OOA-LSSVM鱼鹰算法优化最小…

C++关键词auto详解

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、小思考 随着我们对于C的不断学习&#xff0c;遇到的程序越来越复杂&#xff0c;程序中用到的类型也越来越复杂…