Remix 开发小技巧(二)

news/2024/7/15 18:33:42 标签: javascript, 前端, react.js

由于我计划将来从事其他 Remix 项目,我想写几篇博文作为自己的指南。这是一项正在进行的工作,我将在学习新事物时对其进行更新。

文章目录

  • 创建自定义 Remix Hook 访问来自任何组件的加载器数据
    • 从任何地方访问 Root 加载程序数据
    • 查找所需路由的 ID
    • 非根路由加载程序数据
    • 根路由加载程序数据
  • 定义 404
    • 定义根错误边界
    • 处理其他错误
    • 不要抛出输入错误
    • 对用户体验的一条建议
  • 创建可重用的表单组件

创建自定义 Remix Hook 访问来自任何组件的加载器数据

访问加载器数据的典型方法是 useLoaderData 钩子,它将获取最近的加载器的数据。这是通过 React Context 实现的,因此您也可以在组件中使用它。

这里有一把脚枪:如果您尝试在不同的路由中重用该组件,它将获取该路由的加载器数据。

为了解决这个问题,Remix 提供了一个 useRouteLoaderData 钩子,让你通过传递其 ID 来获取任何活动路由的加载器数据。请务必注意,这不会导致任何新的加载程序运行,它只是获取数据(如果可用)。

从任何地方访问 Root 加载程序数据

根路由始终处于活动状态,因此它是存储您希望在任何地方可用的数据的好地方。

您可以使用它从任何路由访问根加载程序数据,但如果需要类型安全,则需要导入根加载程序的类型并将其作为类型参数传递给 useRouteLoaderData

// some-other-route.tsx
import type { loader as rootLoader } from "./root.tsx"
export default function SomeOtherRoute() {
  const rootLoaderData =
    useRouteLoaderData<typeof rootLoader>("root")
  // …
}

我不是这种方法的忠实粉丝,因为我每次使用它时都必须导入加载器(通常带有别名)并记下路由 ID。虽然根的 ID 始终 “root” 是 ,但其他路由具有不同的 ID。在实践中,t 很容易在 ID 和加载器类型之间出现不匹配。

与其这样做,我更喜欢在我的 root.tsx 文件中创建一个自定义钩子,其中加载器已经可用,并将其导出以供其他文件使用。

// root.tsx
export async function loader() {}

export function useRootLoaderData() {
  return useRouteLoaderData<typeof loader>("root")
}

查找所需路由的 ID

根据您使用的路由约定,特定路由的 ID 可以是任何内容。与其试图记住模式或挖掘清单,我喜欢使用 useMatches() 钩子。

由于 useMatch 返回所有活动路由的数组,因此它还形成了一个完整的列表,其中包含 useRouteLoaderData 将在给定页面上使用的路由。

// some-other-route.tsx
import { useRootLoaderData } from "./root.tsx"
export default function SomeOtherRoute() {
  // temporary just to discover the route IDs
  const matches = useMatches()
  console.log(matches)
  // [
  //   {
  //     id: 'root',
  //     pathname: '/',
  //     data: [Object],
  //   },
  //   {
  //     id: '_layout',
  //     pathname: '/',
  //     data: [Object],
  //   },
  //   {
  //     id: '_layout.content',
  //     pathname: '/content',
  //     data: null,
  //   },
  //   {
  //     id: '_layout.content.$slug',
  //     pathname: '/content/remix-route-loader-data',
  //     data: [Object],
  //   }
  // ]
}

使用中的第一个路由始终是根路由,最后一个路由始终是当前路由。当你这样做时,你通常会寻找中间的那些。在本例中,您可以看到布局路由的 ID 为 _layout ,因此我将使用这种 ID。

知道 ID 后,可以删除此代码。

如果路由没有加载程序,则数据将为 null,但如果尝试访问未处于活动状态的路由,则数据将未定义。这几乎总是开发人员的错误(在错误的位置使用组件),因此,如果未定义,最好抛出错误。

非根路由加载程序数据

// routes/_layout.tsx
export async function loader() {}
export function useLayoutLoaderData() {
  const data = useRouteLoaderData<typeof loader>("_layout")
  if (data === undefined) {
    throw new Error(
      "useLayoutLoaderData must be used within the _layout route or its children",
    )
  }
  return data
}

根路由加载程序数据

// root.tsx
export async function loader() {}
export function useRootLoaderData() {
  return useRouteLoaderData<typeof loader>("root")
}

定义 404

首先,让我们介绍所有网页应用程序中最常见的错误之一,404 Not Found。

您需要做几件事:

  • 在路由文件夹的根目录下创建一个新文件 $.tsx 对于找不到其路由的 URL,这实际上是一个包罗万象的。
  • 在加载器中使用 404 进行响应 Remix 还不知道您正在使用此包罗万象的路由来处理 404 错误,因此它不会像搜索引擎所期望的那样使用 http 状态代码 404 进行响应。因此,添加一个加载程序并使用 404 进行响应。
export function loader() {
  return new Response("Not Found", {
    status: 404,
  });
}
  • 定义您的页面:我正在使用一个简单的组件来显示错误,但如果您愿意,您可以使您的页面更好。
export default function NotFoundPage() {
  return <BigStatusMessage
    type="error"
    message="Not Found"
    title="404"
   />;
}

定义根错误边界

这是最后的手段错误边界,当不存在其他错误边界时,将针对错误触发它。您应该拥有的不仅仅是根错误边界。

// root.tsx
export function ErrorBoundary() {
  const error = useRouteError();
  return (
    <html>
      <head>
        <title>Oh no!</title>
        <Meta />
        <Links />
      </head>
      <body>
        {/* add the UI you want your users to see */}
        <Scripts />
      </body>
    </html>
  );
}

使用 isRouteErrorResponse 检查它是否为 HTTP 错误响应。例如,您可以使用它来检查 error.status。
 
isRouteErrorResponse 将为加载程序和操作中抛出的响应返回 true。请注意,在我们的 404 页面加载器中,我们只是简单地返回响应而不是抛出它?

处理其他错误

关于如何以及在何处放置其他错误边界的文章已经写了很多,所以我会保持这一点。但我的经验法则是:

您不需要为每个方案使用错误边界

无论在哪里定义 ,都应该定义边界

请记住,只有当出现加载程序错误时,才会在服务器上呈现 ErrorBound。如果存在操作错误,则仅在客户端上呈现该错误。为什么这很重要?如果要天真地捕获和跟踪边界中的错误,则需要实现一些逻辑来确定错误类型。

不要抛出输入错误

首先,您不希望使用 ErrorBoundary 处理输入错误。理想情况下,您希望在用户输入旁边处理它们。

其次,抛出错误将触发handleError,如果您设置了跟踪,则将向错误跟踪服务报告所有输入错误。我向你保证,你不需要知道什么时候有人忘记输入有效的电子邮件,如果你真的需要它,它可能应该是分析的一部分。

对用户体验的一条建议

每当您显示错误时,请为用户提供有关问题所在以及如何解决错误的指导。

例如,不要只显示“出了点问题”。而是显示类似以下内容:“找不到页面<返回主页>”

在这里插入图片描述

如果你已经做到了这一点,希望你的错误处理现在处于更好的状态。

创建可重用的表单组件

假设我们要为我们的应用程序构建一个自定义 Form 组件,这个 Form 将在内部执行一些操作,例如使用 CSRF 令牌呈现隐藏的输入,接受重定向以在正文中发送,或者只是应用所有表单都将具有的一些样式。

构建这样的东西的第一种方法可能是包装 Remix 的 Form 组件。

import type { FormProps as RemixFormProps } from "@remix-run/react";

import { Form as RemixForm } from "@remix-run/react";

type FormProps = RemixFormProps & {
  redirectTo?: string;
};

export function Form({ redirectTo, children, ...props }: FormProps) {
  return (
    <RemixForm {...props} className="some classes">
      {redirectTo && (
        <input type="hidden" name="redirectTo" value={redirectTo} />
      )}
      <CSRFTokenInput />
      {children}
    </RemixForm>
  );
}

这将起作用,直到您想用 fetcher.Form .为了解决这个问题,我们可以接受组件作为 prop!

type FormProps = RemixFormProps & {
  redirectTo?: string;
  form?: React.ComponentType<FormProps>;
};

export function Form({
  redirectTo,
  children,
  // here we default to RemixForm
  form: Component = RemixForm,
  ...props
}: FormProps) {
  return (
    <Component {...props} className="some classes">
      {/* content here */}
    </Component>
  );
}

最后,我们可以在路由或任何其他组件中使用它:

function Route() {
  let fetcher = useFetcher();

  return (
    <>
      <Form />; // with the default
      <Form form={fetcher.Form} />; // with a fetcher
    </>
  );
}

此模式与 remix-form 用于让您传递 Form 或提取器的模式相同。表单,来自 Remix 或 React Router。


http://www.niftyadmin.cn/n/5087007.html

相关文章

ubuntu下使用gcc编译c程序: “error: stray ‘\357’ in program“

现象&#xff1a; ubuntu下使用gcc编译c程序: “error: stray ‘\357’ in program“ 尝试查找原因&#xff1a;打开从windos直接粘贴c程序到ubuntu的c代码&#xff0c;发现多了 <200b>&#xff1a; 方案&#xff1a;尝试在vim编辑器删除&#xff0c;多出来的字符后编译…

Godot快速精通-从看懂英文文档开始-翻译插件

视频教程地址&#xff1a;https://www.bilibili.com/video/BV1t8411q7hw/ 大家好&#xff0c;我今天要和大家分享的是如何快速精通Godot&#xff0c;众所周知&#xff0c;一般一个开源项目都会有一个文档&#xff0c;对于有一定基础或者是理解能力强的同学&#xff0c;看文档比…

atoi函数及其模拟实现

这个函数的功能是将字符串转换为整形&#xff0c;那么具体是怎么样的呢 先看几个例子&#xff1a; 有一个转换为整形的最大值 刚开始就是非法字符 因此&#xff0c;我们模拟实现时&#xff0c;要考虑以上几种非法输入情况&#xff1a; 1.空字符串 2.空白字符 3.处理-号 4.过大…

十六、【橡皮擦工具组】

文章目录 橡皮擦背景橡皮擦1. 一次取样2. 连续取样3. 取样背景色板 魔术橡皮擦 橡皮擦 橡皮擦跟我们平常生活中所用的橡皮擦是一样&#xff0c;它是将图层的内容擦除,只剩下空白部分。另外当我们按住Alt的键去擦除空白部分的时候&#xff0c;也可以将背景的部分显示出来。 另…

Open CV 3D Python 环境搭建

1、安装Windows-Python环境 下载exe 并安装 https://python.p2hp.com/downloads/windows/index.html 安装路径随意, 基本一路默认,下一步、下一步 注意有个钩&#xff1a;添加到环境变量 检测是否成功安装Python 环境 CMD输入python 2、安装OpenCV -Python 包来自清华大学…

Go持续改进与代码审查

通过Golang提高软件质量 在快节奏的软件开发世界中&#xff0c;保持领先至关重要。在实现软件工程卓越的基石之一是持续改进的实践&#xff0c;而在这个旅程中的一个关键工具是代码审查过程。在本文中&#xff0c;我们将深入探讨持续改进的重要性&#xff0c;并探讨代码审查在…

nginx实现灰度上线(InsCode AI 创作助手)

要基于Nginx实现灰度上线&#xff0c;有以下三种方法&#xff1a; 权重分发&#xff1a;使用Nginx的upstream模块来设置不同服务器的权重。将一部分请求分发给新版本服务器&#xff0c;另一部分请求分发给旧版本服务器。这可以通过以下方式实现&#xff1a; http {upstream bac…

【React】01-React的入门

文章目录 1.1 React简介1.1.1 官网1.1.2 介绍描述1.1.3 React的特点1.1.3 React高效的原因 1.2.React的基本使用1.2.2.相关js库1.2.3.创建虚拟DOM的两种方式1.2.4.虚拟DOM与真实DOM 1.3.React JSX1.3.1.效果jsx语法规则&#xff1a;1.3.2.JSX1.3.3.渲染虚拟DOM(元素)1.3.4.JSX练…