【React Router】初识路由(中)

加载数据

这一节主要强调 URL、布局和数据 的解耦。

在根模块文件中创建并导出一个加载器函数,并配置到路由。

getContacts() 是我们自己封装的数据请求 API,新增的数据暂时存储到 localforage。

export async function loader() {const contacts = await getContacts();return { contacts };
}

路由配置 loader

// 配置 loader
loader: rootLoader,

数据渲染,使用 内置的 useLoaderData() 方法

const { contacts } = useLoaderData();

数据写入

提交 HTML 表单时,它会在浏览器中引起导航事件,就像我们点击某个链接一样。而链接和提交表单的唯一的区别在于请求:链接只能更改 URL,而表单还可以更改请求方式(GET 与 POST)和请求体(POST 表单数据)。

如果没有客户端路由(也就是react-router这类的client-side routing),浏览器会自动序列化表单数据,并以不同的方式发送给服务器。对于POST请求,数据会作为请求体(request body)发送给服务器;而对于GET请求,数据会作为URLSearchParams(URL查询参数)的形式附加在URL上发送给服务器。

React Router的行为与此类似,但它不会将请求发送到服务器,而是使用客户端路由并将请求发送到路由操作action进行处理。

我们使用 React Router 封装的 <Form>,创建用户。

<Form method="post"><button type="submit">New</button>
</Form>
export async function action() {const contact = await createContact();return redirect(`/contacts/${contact.id}/edit`);
}

像 loader 一样,action 创建后也需要挂载在路由上。action: rootAction

可以简单的理解为 loader 是需要获取数据时触发的,action 则是修改数据时触发的。

创建用户信息,使用 Form submit之后,我们发现Reat Router 的<Form> 自动阻止了浏览器发送请求到服务器,而是将其发送到我们指定的路由操作action中 。然后自动触发数据重新验证的过程,也就意味着所有使用 useLoaderData 钩子的组件都会自动更新!并且UI会自动与数据保持同步!避免我们使用 useState 、 onSubmit 和 useEffect 这些操作,极大地简化了我们的操作。

URL 参数

path: 'contacts/:contactId',

:contactId URL 段。冒号 ( : ) 为“动态段”。动态段将匹配 URL 该位置上的动态(变化)值,如联系人 ID。我们将 URL 中的这些值称为“URL 参数”,简称 “params”。这些params将作为键值对传递给加载器loader,值将作为 params.contactId 传递(可以在 loader上使用)。

当然,每个组件中的 loader, action 等都需要在路由中进行配置,而且路由通常都有自己的 laoder 等,只不过他们有时可能会相同。

export async function loader({ params }) {const contact = await getContact(params.contactId);return { contact };
}export async function Contact() {const { contact } = useLoaderData();// ...
}

更新数据

<inputplaceholder="First"aria-label="First name"type="text"name="first"defaultValue={contact.first}
/>

如果没有 JavaScript,当提交表单时,浏览器会创建FormData对象,并将其设置为请求的主体(body),然后将请求发送到服务器。

如前所述,React Router 阻止了这种默认行为,而是将请求发送到我们自己想要的操作,也就是自己在组件中定义的 action中,同时包括FormData。表单中的每个字段都可以通过 formData.get(name) 访问。

这里还使用Object.fromEntries将数据全部收集到一个对象中,这正是后面自定义的 updateContact 函数想要的。

export async function action({ request, params }) {const formData = await request.formData();const updates = Object.fromEntries(formData);await updateContact(params.contactId, updates);return redirect(`/contacts/${params.contactId}`);
}

loaderaction都可以返回Response(因为它们都收到了Request!)。

redirect辅助函数只是为了更方便地返回response,告诉应用程序更改位置。

活动链接样式

<NavLinkto={`contacts/${contact.id}`}className={({ isActive, isPending }) =>isActive? "active": isPending? "pending": ""}
>

<NavLink> 是一种特殊的 <Link> ,它知道自己是否处于 “激活”、"待定 "或 "过渡 "状态。当用户访处于NavLink指定的URL时,isActive将为true。当链接即将被激活时(数据仍在加载中), isPending 将为 true。

loading 界面

当用户浏览应用时,React Router 会在为下一页加载数据时保留旧页面。也就是说这种数据模型具有客户端缓存,因此第二次导航到之前已经访问过的路由时速度会很快。

useNavigation返回当前导航状态:可以是"idle" | "submitting" | "loading"

const navigation = useNavigation();
// ...
navigation.state === "loading" ? "loading" : ""

删除数据

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

action 指向 "destroy" 。与 <Link to> 一样, <Form action> 也可以接收一个相对值。由于表单是在 contact/:contactId 中呈现的,因此点击 destroy 的相对操作将把表单提交到 contact/:contactId/destroy ,所以我们只需要在 contact/:contactId/destroy 路由下指定删除操作即可。比如:

export async function action({ params }) {await deleteContact(params.contactId);return redirect("/");
}
{path: 'contacts/:contactId/destroy',action: destroyAction,
},

详细说明,当用户点击提交按钮时:

  1. <Form> 会阻止浏览器向服务器发送新 POST 请求的默认行为,而是通过客户端路由创建一个 POST 请求来模拟浏览器的行为
  2. <Form action="destroy"> 匹配道路新路由 "contacts/:contactId/destroy" ,并向其发送请求
  3. 在操作重定向后,React Router 会调用页面上所有数据的loader,以获取最新值(这就是 “重新验证”)。 useLoaderData 返回新值,并导致组件更新!

捕获错误

export async function action({ params }) {await deleteContact(params.contactId);throw new Error("oh dang!");return redirect("/");
}

虽然我这个操作中 return redirect("/"); 根本就不会执行,但这只是为了说明如何捕获错误😁。

这样操作完成之后,发现直接如今了错误页,必须刷新才会重新加载恢复正常显示。

因此需要在路由中进行捕获:

{path: 'contacts/:contactId/destroy',action: destroyAction,errorElement: <div>Oops! There was an error.</div>,
},

因为销毁路由有自己的errorElement,并且是根路由的子路由,因此错误会在此处路由而不是根路由上呈现。这些错误会以最近的 errorElement 冒泡。只要在根路径上有一个,添加多少都行。

索引路由

如果在父路由的路径上, <Outlet> 由于没有子路由匹配,所以没有任何内容可以呈现。可以把索引路由看作是填补这一空白的默认子路由。

定义一个索引路由的页面模块 index。然后在子路由上添加:

{ index: true, element: <Index /> },

这将告诉路由,当用户位于父路由的确切路径时,路由器将匹配并呈现此路由,以保证在 <Outlet> 中没有其他子路由需要呈现时页面不为空。

导航

用一个取消功能来说明。

const navigate = useNavigate();
<buttontype="button"onClick={() => {navigate(-1);}}
>Cancel</button>

这里的 <button type="button"> 虽然看似多余,却是防止按钮提交表单的 默认 HTML 行为。所以按钮上没有event.preventDefault


以上说明中路由:

// 创建路由
const router = createBrowserRouter([{path: '/',// 将<Root>设置为根路由elementelement: <Root/>,// 将<ErrorPage>设置为根路由上的errorElementerrorElement: <ErrorPage/>,// 配置 loaderloader: rootLoader,// 配置 actionaction: rootAction,// 子路由children: [{ index: true, element: <Index /> },{path: 'contacts/:contactId',element: <Contact/>,loader: contactLoader},{path: "contacts/:contactId/edit",element: <EditContact/>,loader: contactLoader,action: editAction},{path: 'contacts/:contactId/destroy',action: destroyAction,errorElement: <div>Oops! There was an error.</div>,},]},
])

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

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

相关文章

Python学习从0开始——005数据结构

Python学习从0开始——005数据结构 一、列表list二、元组和序列三、集合四、字典五、循环技巧六、条件控制七、序列和其它类型的比较 一、列表list 不是所有数据都可以排序或比较。例如&#xff0c;[None, ‘hello’, 10] 就不可排序&#xff0c;因为整数不能与字符串对比&…

【C 数据结构】线性表

文章目录 【 1. 线性表 】【 2. 顺序存储结构、链式存储结构 】【 3. 前驱、后继 】 【 1. 线性表 】 线性表&#xff0c;全名为线性存储结构&#xff0c;线性表结构存储的数据往往是可以依次排列的&#xff08;不考虑数值大小顺序&#xff09;。 例如&#xff0c;存储类似 {1…

蓝桥杯-数组分割

问题描述 小蓝有一个长度为 N 的数组 A 「Ao,A1,…,A~-1]。现在小蓝想要从 A 对应的数组下标所构成的集合I 0,1,2,… N-1 中找出一个子集 民1&#xff0c;那么 民」在I中的补集为Rz。记S∑reR 4&#xff0c;S2∑rERA,&#xff0c;我们要求S、和 S,均为偶数&#xff0c;请问在这…

c语言-----数组知识汇总

前言 本文为我学习数组知识点之后&#xff0c;对c语言的数组部分进行的知识点汇总。 简单数组介绍 简单来说&#xff0c;数组就是一个数据组&#xff0c;像一个箱子&#xff0c;里面放有多个数据。 [1,2,3,4,5] 数组的定义 基础定义 语法&#xff1a; 数据类型 数组名[数组…

代码随想录-算法训练营day12【休息,复习与总结】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 ● day 12 周日休息&#xff08;4.14&#xff09; 目录 复习与总结 0417_图论-太平洋大西洋水流问题 0827_图论-最大人工岛 复习与总结 二刷做题速度提升了一大截&#xff0c;ヾ(◍∇◍)&#xff89;&#xff9e;加…

基于SpringBoot实现的在线拍卖系统

系统开发环境 编程语言&#xff1a;Java数据库&#xff1a;MySQL容器&#xff1a;Tomcat工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统实现 管理员功能模块 首页 修改密码 用户管理 商品类型管理 拍卖商品 竞拍公告 轮播图 历史竞拍管理 竞拍订单管理 留言板管理 用户…

在windows系统中【.gz.tar】和【.whl】文件分别应该怎么下载到conda的某个虚拟环境中

在 Windows 系统中&#xff0c;你可以按照以下步骤将 .gz.tar 和 .whl 文件下载到 Conda 的某个虚拟环境中&#xff1a; 激活虚拟环境&#xff1a;打开 Anaconda Prompt 或者命令行窗口&#xff0c;使用以下命令激活你想要安装文件的虚拟环境&#xff1a; conda activate <虚…

多输入多输出 | Matlab实现XGboost多输入多输出预测

多输入多输出 | Matlab实现XGboost多输入多输出预测 目录 多输入多输出 | Matlab实现XGboost多输入多输出预测预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 Matlab实现XGboost多输入多输出预测 1.data为数据集&#xff0c;10个输入特征&#xff0c;3个输出变量…

Unity数据持久化—Json存档

项目需求为&#xff1a; 1.实现存档列表&#xff0c;显示存档截图&#xff0c;可以查看之前保存的所有存档 2.点击存档直接加载到场景 首先&#xff0c;定义两个类&#xff0c;用于声明存档列表和存档所需要的List [System.Serializable] public class SaveData {//存储目标…

使用 vue3-sfc-loader 加载远程Vue文件, 在运行时动态加载 .vue 文件。无需 Node.js 环境,无需 (webpack) 构建步骤

加载远程Vue文件 vue3-sfc-loader vue3-sfc-loader &#xff0c;它是Vue3/Vue2 单文件组件加载器。 在运行时从 html/js 动态加载 .vue 文件。无需 Node.js 环境&#xff0c;无需 (webpack) 构建步骤。 主要特征 支持 Vue 3 和 Vue 2&#xff08;参见dist/&#xff09;仅需…

macad.presentation解析events,helpers,multicover,resourcesextention

1.events using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop;namespace Macad.Presentation {// 该类用于启用鼠标…

CentOS 7源码包与RPM包软件安装详解

CentOS 7源码包与RPM包软件安装详解 在CentOS 7中,软件包的安装主要有两种方式:源码包安装和RPM包安装。这两种方式各有优缺点,适用于不同的场景和需求。 一、源码包安装 源码包安装指的是从软件的源代码开始,通过编译过程将源代码转换为机器语言,进而实现软件的安装。…

UDP实现Mini版在线聊天室

实现原理 只有当客户端先对服务器发送online消息的时候&#xff0c;服务器才会把客户端加入到在线列表。当在线列表的用户发消息的时候&#xff0c;服务器会把消息广播给在线列表中的所有用户。而当用户输入offline时&#xff0c;表明自己要下线了&#xff0c;此时服务器把该用…

服务器docker应用一览

文章目录 一、需求概况二、业务流程三、运行效果四、实现过程1. 基础前提2. 源码放送3.核心代码4. 项目打包5.部署步骤 一、需求概况 现有某云主机服务器&#xff0c;用来做项目演示用&#xff0c;上面运行了docker应用&#xff0c;现希望有一总览页面&#xff0c;用来展示部署…

【话题】选择成为一名程序员的原因

选择成为一名程序员&#xff0c;可以是出于多种原因&#xff0c;包括兴趣驱动和职业发展考虑。 兴趣驱动&#xff1a;很多人对计算机科学、编程和技术充满热情。他们喜欢解决问题&#xff0c;享受创造新事物的过程&#xff0c;以及在编写代码时所面临的挑战。对于这些人来说&am…

HC-SR04(超声波模块)

工具 1.Proteus 8 仿真器 2.keil 5 编辑器 原理图 讲解 简介 HC-SR04超声波模块是一种常用的测距模块&#xff0c;通过不断检测超声波发射后遇到障碍物所反射的回波&#xff0c;从而测出发射和接收回波的时间差&#xff0c;并据此求出距离。它主要由两个压电陶瓷超声传感器…

Centos7查看内存使用情况

Centos7查看内存使用情况 free -b&#xff1a;以字节为单位显示内存使用情况。-k&#xff1a;以KB为单位显示内存使用情况&#xff08;默认选项&#xff09;。-m&#xff1a;以MB为单位显示内存使用情况。-g&#xff1a;以GB为单位显示内存使用情况。-t&#xff1a;在输出的最…

C++知识点总结(29):递归练习

一、满足条件的值 1. 审题 已知&#xff1a; S 1 2 4 7 11 16 … S12471116… S12471116… 递归求解刚好大于等于 5000 5000 5000 时 S S S 的值。 2. 参考答案 #include <iostream> using namespace std;// 定义递归函数&#xff0c;计算第x个数的值 int f(…

【Python】使用OPC UA创建数据服务器

目录 准备工作服务器设置创建或获取节点设置节点值启动服务器查看服务器客户端总结 在工业自动化和物联网&#xff08;IoT&#xff09;领域&#xff0c;OPC UA&#xff08;开放平台通信统一架构&#xff09;已经成为一种广泛采用的数据交换标准。它提供了一种安全、可靠且独立于…

Mixed-Query Transformer:统一的图像分割架构

Mixed-Query Transformer:统一的图像分割架构 摘要IntroductionRelated WorkMethodMQ-Former ArchitectureObject Query Strategies Mixed-Query Transformer: A Unified Image Segmentation Architectur 摘要 在现有的一体化图像分割模型中&#xff0c;要么在多个任务上采用统…