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 (You Only Live Once,你只活一次)。

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