shouldRevalidate
On this page

shouldRevalidate

此函数允许应用优化在操作后以及客户端导航时应重新加载哪些路由数据。

¥This function lets apps optimize which routes data should be reloaded after actions and for client-side navigations.

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 始终与服务器状态保持同步。

¥During client-side transitions, Remix will optimize reloading of routes that are already rendering, like not reloading layout routes that aren't changing. In other cases, like form submissions or search param changes, Remix doesn't know which routes need to be reloaded, so it reloads them all to be safe. This ensures your UI always stays in sync with the state on your server.

此函数允许应用在 Remix 即将重新加载路由时返回 false,从而进一步优化。如果你在路由模块上定义此功能,Remix 将在每次导航和调用操作后的每次重新验证时遵循你的功能。同样,如果你操作错误,这可能会导致你的 UI 与服务器不同步,所以请小心。

¥This function lets apps further optimize by returning false when Remix is about to reload a route. If you define this function on a route module, Remix will defer to your function on every navigation and every revalidation after an action is called. Again, this makes it possible for your UI to get out of sync with your server if you do it wrong, so be careful.

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

¥fetcher.load calls also revalidate, but because they load a specific URL, they don't have to worry about route param or URL search param revalidations. fetcher.load's only revalidate by default after action submissions and explicit revalidation requests via useRevalidator.

actionResult

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

¥When a submission causes the revalidation, this will be the result of the action—either action data or an error if the action failed. It's common to include some information in the action result to instruct shouldRevalidate to revalidate or not.

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:

¥By default, Remix doesn't call every loader all the time. There are reliable optimizations it can make by default. For example, only loaders with changing params are called. Consider navigating from the following URL to the one below it:

  • /projects/123/tasks/abc

  • /projects/123/tasks/def

Remix 只会调用 tasks/def 的加载器,因为 projects/123 的参数没有变化。

¥Remix will only call the loader for tasks/def because the param for projects/123 didn't change.

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

¥It's safest to always return defaultShouldRevalidate after you've done your specific optimizations that return false, otherwise your UI might get out of sync with your data on the server.

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

  return defaultShouldRevalidate;
}

这更危险,但 YOLO:

¥This is more dangerous, but YOLO:

export function shouldRevalidate() {
  return whateverConditionsYouCareAbout;
}

currentParams

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

¥These are the URL params from the URL that can be compared to the nextParams to decide if you need to reload or not. Perhaps you're using only a partial piece of the param for data loading, you don't need to revalidate if a superfluous part of the param changed.

例如,假设一个事件标签包含一个 ID 和一个用户友好的标题:

¥For instance, consider an event slug with the id and a human-friendly title:

  • /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。

¥This is the url the navigation started from.

nextParams

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

¥In the case of navigation, these are the URL params from the next location the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentParams.

nextUrl

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

¥In the case of navigation, this is the URL the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentUrl.

formMethod

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

¥The method (probably "GET" or "POST") used from the form submission that triggered the revalidation.

formAction

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

¥The form action (<Form action="/somewhere">) that triggered the revalidation.

formData

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

¥The data submitted with the form that triggered the revalidation.

用例

¥Use Cases

永不重新加载根目录

¥Never reloading the root

根加载器通常会返回一些永远不会改变的数据(例如环境变量)并将其发送到客户端应用。在这种情况下,你无需再次调用根加载器。对于这种情况,你可以直接使用 return false

¥It's common for root loaders to return data that never changes, like environment variables, to be sent to the client app. In these cases you never need the root loader to be called again. For this case, you can simply 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 将不再因任何原因向根加载器发出请求,例如在表单提交后、搜索参数更改后等等。

¥With this in place, Remix will no longer make a request to your root loader for any reason, not after form submissions, not after search param changes, etc.

忽略搜索参数

¥Ignoring search params

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

¥Another common case is when you've got nested routes and a child component has a feature that uses the search params in the URL, like a search page or some tabs with state you want to keep in the search params.

考虑以下路由:

¥Consider these routes:

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

假设 UI 如下所示:

¥And let's say the UI looks something like this:

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

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

¥The $projectId.activity.tsx loader can use the search params to filter the list, so visiting a URL like /projects/design-revamp/activity?search=image could filter the list of results. Maybe it looks something like this:

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 会采取最安全的做法,在搜索参数发生变化时重新加载页面上的所有路由。

¥This is great for the activity route, but Remix doesn't know if the parent loader, $projectId.tsx also cares about the search params. That's why Remix does the safest thing and reloads all the routes on the page when the search params change.

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

¥In this UI, that's wasted bandwidth for the user, your server, and your database because $projectId.tsx doesn't use the search params. Consider that our loader for $projectId.tsx looks something like this:

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

有很多方法可以做到这一点。应用中的其余代码很重要,但理想情况下,你不需要考虑要优化的 UI(搜索参数的变化),而是关注加载器所关注的值。在我们的例子中,它只关心 projectId,因此我们可以检查两件事:

¥There are a lot of ways to do this. The rest of the code in the app matters, but ideally, you don't think about the UI you're trying to optimize (the search params changing) but instead look at the values your loader cares about. In our case, it only cares about the projectId, so we can check two things:

  • 参数保持不变吗?

    ¥did the params stay the same?

  • 它是 GET 而不是一个突变吗?

    ¥was it a GET and not a mutation?

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

¥If the params didn't change, and we didn't do a POST, then we know our loader will return the same data it did last time, so we can opt out of the revalidation when the child route changes the search params.

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

  return defaultShouldRevalidate;
}
Remix v2.17 中文网 - 粤ICP备13048890号