React Router v7 已发布。 查看文档
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"),
        },
      },
    })
  );
}

这对活动路由很有好处,但 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