shouldRevalidate
本页内容

shouldRevalidate

此函数允许应用程序优化操作后和客户端导航后应重新加载哪些路由数据。

import type { ShouldRevalidateFunction } from "@remix-run/react";

export const shouldRevalidate: ShouldRevalidateFunction = ({
  actionResult,
  currentParams,
  currentUrl,
  defaultShouldRevalidate,
  formAction,
  formData,
  formEncType,
  formMethod,
  nextParams,
  nextUrl,
}) => {
  return true;
};

此功能是一种 *额外* 的优化。通常,Remix 的设计已经优化了哪些加载器需要何时调用。当您使用此功能时,您可能会使您的 UI 与服务器不同步。谨慎使用!

在客户端转换期间,Remix 将优化正在渲染的路由的重新加载,例如不重新加载未更改的布局路由。在其他情况下,例如表单提交或搜索参数更改,Remix 不知道哪些路由需要重新加载,因此它会全部重新加载以确保安全。这确保您的 UI 始终与服务器上的状态保持同步。

此函数允许应用程序通过在 Remix 即将重新加载路由时返回 false 来进一步优化。如果您在路由模块上定义此函数,Remix 将在每次导航和每次操作调用后的重新验证时都使用您的函数。同样,如果您操作不当,这可能会导致您的 UI 与服务器不同步,因此请谨慎操作。

fetcher.load 调用也会重新验证,但由于它们加载特定的 URL,因此无需担心路由参数或 URL 搜索参数的重新验证。fetcher.load 仅在操作提交后和通过 useRevalidator 显式请求重新验证后默认重新验证。

actionResult

当提交导致重新验证时,这将是操作的结果——无论是操作数据还是操作失败时的错误。通常在操作结果中包含一些信息来指示 shouldRevalidate 是否重新验证。

export async function action() {
  await saveSomeStuff();
  return { ok: true };
}

export function shouldRevalidate({
  actionResult,
  defaultShouldRevalidate,
}) {
  if (actionResult?.ok) {
    return false;
  }
  return defaultShouldRevalidate;
}

defaultShouldRevalidate

默认情况下,Remix 不会一直调用每个加载器。它可以默认进行可靠的优化。例如,仅调用具有更改参数的加载器。考虑从以下 URL 导航到其下方的 URL

  • /projects/123/tasks/abc
  • /projects/123/tasks/def

Remix 将仅调用 tasks/def 的加载器,因为 projects/123 的参数没有更改。

在完成返回 false 的特定优化后,始终返回 defaultShouldRevalidate 是最安全的,否则您的 UI 可能会与服务器上的数据不同步。

export function shouldRevalidate({
  defaultShouldRevalidate,
}) {
  if (whateverConditionsYouCareAbout) {
    return false;
  }

  return defaultShouldRevalidate;
}

这更危险,但 YOLO

export function shouldRevalidate() {
  return whateverConditionsYouCareAbout;
}

currentParams

这些是来自 URL 的 URL 参数,可以与 nextParams 进行比较以决定是否需要重新加载。也许您只使用参数的一部分进行数据加载,如果参数的无关部分发生更改,则无需重新验证。

例如,考虑一个包含 ID 和用户友好标题的事件 slug

  • /events/blink-182-united-center-saint-paul--ae3f9
  • /events/blink-182-little-caesars-arena-detroit--e87ad
export async function loader({
  params,
}: LoaderFunctionArgs) {
  const id = params.slug.split("--")[1];
  return loadEvent(id);
}

export function shouldRevalidate({
  currentParams,
  nextParams,
  defaultShouldRevalidate,
}) {
  const currentId = currentParams.slug.split("--")[1];
  const nextId = nextParams.slug.split("--")[1];
  if (currentId === nextId) {
    return false;
  }

  return defaultShouldRevalidate;
}

currentUrl

这是导航开始时的 URL。

nextParams

在导航的情况下,这些是用户请求的下一个位置的 URL 参数。有些重新验证不是导航,因此它将与 currentParams 相同。

nextUrl

在导航的情况下,这是用户请求的 URL。有些重新验证不是导航,因此它将与 currentUrl 相同。

formMethod

触发重新验证的表单提交所使用的方法(可能是 "GET""POST")。

formAction

触发重新验证的表单操作(<Form action="/somewhere">)。

formData

触发重新验证的表单提交的数据。

使用案例

从不重新加载根路由

根加载器通常返回永不更改的数据,例如要发送到客户端应用程序的环境变量。在这些情况下,您不需要再次调用根加载器。对于这种情况,您可以简单地 return false

export const loader = async () => {
  return json({
    ENV: {
      CLOUDINARY_ACCT: process.env.CLOUDINARY_ACCT,
      STRIPE_PUBLIC_KEY: process.env.STRIPE_PUBLIC_KEY,
    },
  });
};

export const shouldRevalidate = () => false;

有了它,Remix 将不再出于任何原因向您的根加载器发出请求,无论是表单提交后还是搜索参数更改后等。

忽略搜索参数

另一种常见情况是,当您有嵌套路由并且子组件具有使用 URL 中搜索参数的功能时,例如搜索页面或一些带有您希望保留在搜索参数中的状态的选项卡。

考虑这些路由

├── $projectId.tsx
└── $projectId.activity.tsx

并假设 UI 如下所示

+------------------------------+
|    Project: Design Revamp    |
+------------------------------+
|  Tasks | Collabs | >ACTIVITY |
+------------------------------+
|  Search: _____________       |
|                              |
|  - Ryan added an image       |
|                              |
|  - Michael commented         |
|                              |
+------------------------------+

$projectId.activity.tsx 加载器可以使用搜索参数过滤列表,因此访问类似 /projects/design-revamp/activity?search=image 的 URL 可以过滤结果列表。也许它看起来像这样

export async function loader({
  params,
  request,
}: LoaderFunctionArgs) {
  const url = new URL(request.url);
  return json(
    await exampleDb.activity.findAll({
      where: {
        projectId: params.projectId,
        name: {
          contains: url.searchParams.get("search"),
        },
      },
    })
  );
}

这对于 activity 路由来说是很棒的,但 Remix 不知道父加载器 $projectId.tsx *是否也*关心搜索参数。这就是为什么当搜索参数更改时,Remix 会执行最安全的操作并重新加载页面上的所有路由。

在此 UI 中,这对用户、您的服务器和您的数据库来说都是浪费带宽,因为 $projectId.tsx 不使用搜索参数。假设我们 $projectId.tsx 的加载器如下所示

export async function loader({
  params,
}: LoaderFunctionArgs) {
  const data = await fakedb.findProject(params.projectId);
  return json(data);
}

有很多方法可以做到这一点,并且应用程序中的其余代码也很重要,但理想情况下,您不会考虑您尝试优化的 UI(搜索参数更改),而是查看加载器关心的值。在我们的例子中,它只关心 projectId,所以我们可以检查两件事

  • 参数是否保持不变?
  • GET 而不是变异吗?

如果参数没有改变,并且我们没有执行 POST,那么我们知道我们的加载器将返回上次返回的相同数据,因此当子路由更改搜索参数时,我们可以选择退出重新验证。

export function shouldRevalidate({
  currentParams,
  nextParams,
  formMethod,
  defaultShouldRevalidate,
}) {
  if (
    formMethod === "GET" &&
    currentParams.projectId === nextParams.projectId
  ) {
    return false;
  }

  return defaultShouldRevalidate;
}
文档和示例根据以下许可获得许可 MIT