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,一经查实,立即删除!

相关文章

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

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

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

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

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…

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

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

canvas的一些基础

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

十分钟快速上手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 输入信号拆分后通过四个发射通…

智慧食堂预点餐管理系统-计算机毕业设计源码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;程序中用到的类型也越来越复杂…

java接口练习

首先&#xff0c;明确&#xff1a;接口可以提供模糊的方法&#xff0c;方案&#xff1b;那么具体的实现需要另外创建实现类去实现&#xff1b; 另外&#xff1a;明确接口的特点&#xff1a;接口回调&#xff0c;接口的多态性&#xff1b; 具体解释&#xff1a; 接口的特点&a…

转转基于MQ的分布式重试框架设计方案

文章目录 1 背景2 方案3 效果4 可选项5 注意事项6 总结 1 背景 在分布式场景下&#xff0c;为了保障系统的可用性和数据的最终一致性&#xff0c;采用基于消息队列&#xff08;MQ&#xff09;的重试机制是一种常见的解决方案。伪代码如下&#xff1a; /*** 需要保证最终一致性…

phpstudy安装并运行redis

对于一个菜鸟来说&#xff0c;任何一个小步骤都可能研究半天&#xff0c;比如“phpstudy安装并运行redis”这一问题&#xff0c;解决好后第一时间记录下来&#xff0c;方便日后查看&#xff0c;也为遇到同样困难的小伙伴提供个参考&#xff01; 一、phpstudy安装redis 1.打开…

打车代驾APP小程序开发功能有哪些?

随着移动互联网的快速发展&#xff0c;越来越多的人开始使用网约车服务。开发一个网约车、打车、叫车系统已经成为了市场的热门需求。 随着城市化进程的加速和人们出行方式的多样化&#xff0c;传统的公共交通方式已经无法满足人们的出行需求。同时&#xff0c;私家车拥有成本也…

【计算机视觉】万字长文详解:卷积神经网络

以下部分文字资料整合于网络&#xff0c;本文仅供自己学习用&#xff01; 一、计算机视觉概述 如果输入层和隐藏层和之前一样都是采用全连接网络&#xff0c;参数过多会导致过拟合问题&#xff0c;其次这么多的参数存储下来对计算机的内存要求也是很高的 解决这一问题&#x…

PostgreSql和Oracle的事务机制区别以及对程序的影响

前言 几年前IT信息产业的一些核心技术包括架构、产品以及生态都是国外制定&#xff0c;然而自从“遥遥领先”公司被制裁后&#xff0c;国家开始大力支持信息产业“新基建”&#xff0c;自2020年开始市场上涌现出了大量的国产化软件&#xff0c;就国产化数据库而言我所在的公司…

辽宁链家新房数据采集与可视化实现

摘 要 网络爬虫也叫做网络机器人&#xff0c;是一种按照一定的规则&#xff0c;自动地抓取网络信息&#xff0c;进行数据信息的采集与整理的程序或者脚本。随着海量数据的出现&#xff0c;如何快速有效的获取到我们想要的数据成为难题。以房源信息为例&#xff0c;该文使用Pyt…