Usage > Prefetching: fetching before user interaction">Usage > Prefetching: fetching before user interaction">
跳到主要內容

預先擷取

預先擷取的目標是在使用者導覽到某個頁面或嘗試載入已知內容之前擷取資料。

有許多情況您可能想要這麼做,但一些非常常見的用例是

  1. 使用者將滑鼠游標懸停在導覽元素上
  2. 使用者將游標移到清單元素上,該元素為連結
  3. 使用者將游標移到下一個分頁按鈕上
  4. 使用者導覽至某個頁面,而您知道樹狀結構中某些組件會需要該資料。如此一來,您可以防止擷取瀑布。

使用 React Hooks 預先擷取

類似於 useMutation Hook,usePrefetch Hook 也不會自動執行 — 它會傳回一個「觸發函式」,可使用它來啟動行為。

它接受兩個引數:第一個是您在 API 服務中定義的查詢動作的鍵,第二個是兩個選用參數的物件

usePrefetch 簽章
export type PrefetchOptions =
| { force?: boolean }
| {
ifOlderThan?: false | number;
};

usePrefetch<EndpointName extends QueryKeys<Definitions>>(
endpointName: EndpointName,
options?: PrefetchOptions
): (arg: QueryArgFrom<Definitions[EndpointName]>, options?: PrefetchOptions) => void;

自訂 Hook 行為

您可以在宣告 Hook 或在呼叫位置指定這些預先擷取選項。呼叫位置會優先於預設值。

  1. ifOlderThan - (預設值:false | number) - 數字是秒數
    • 如果已指定,它只會在 new Date() 和最後一個 fulfilledTimeStamp 之間的差異大於給定值時執行查詢
  2. force
    • 如果 force: true,它會忽略 ifOlderThan 值(如果已設定),並且即使查詢存在於快取中,也會執行查詢。

觸發函式行為

  1. 觸發函式永遠傳回 void
  2. 如果在宣告或呼叫位置設定 force: true,查詢會無論如何執行。唯一的例外是如果同一個查詢已經在進行中。
  3. 如果沒有指定任何選項,且查詢存在於快取中,則不會執行查詢。
  4. 如果沒有指定任何選項,且查詢不存在於快取中,則會執行查詢。
    • 假設您在樹狀結構中有一個 useQuery Hook,訂閱與您正在預先擷取的同一個查詢
      • useQuery 會傳回 {isLoading: true, isFetching: true, ...rest}
  5. 如果指定了 ifOlderThan 但評估為 false,且查詢在快取中,則不會執行查詢。
  6. 如果指定了 ifOlderThan 且評估為 true,則即使有現有的快取項目,也會執行查詢。
    • 假設您在樹狀結構中有一個 useQuery Hook,訂閱與您正在預先擷取的同一個查詢
      • useQuery 會傳回 {isLoading: false, isFetching: true, ...rest}
usePrefetch 範例
function User() {
const prefetchUser = usePrefetch('getUser')

// Low priority hover will not fire unless the last request happened more than 35s ago
// High priority hover will _always_ fire
return (
<div>
<button onMouseEnter={() => prefetchUser(4, { ifOlderThan: 35 })}>
Low priority
</button>
<button onMouseEnter={() => prefetchUser(4, { force: true })}>
High priority
</button>
</div>
)
}

食譜:立即預先擷取

在某些情況下,您可能想要立即預先擷取資源。您可以在幾行程式碼中實作這項功能

hooks/usePrefetchImmediately.ts
type EndpointNames = keyof typeof api.endpoints

export function usePrefetchImmediately<T extends EndpointNames>(
endpoint: T,
arg: Parameters<(typeof api.endpoints)[T]['initiate']>[0],
options: PrefetchOptions = {},
) {
const dispatch = useAppDispatch()
useEffect(() => {
dispatch(api.util.prefetch(endpoint, arg as any, options))
}, [])
}

// In a component
usePrefetchImmediately('getUser', 5)

不使用 Hooks 預先擷取

如果您未使用 usePrefetch 勾子,可以在任何框架中自行重新建立相同行為。

當按以下方式傳送 prefetch thunk 時,您將看到與 在此處說明 完全相同的行為。

非勾子預取範例
store.dispatch(
api.util.prefetch(endpointName, arg, { force: false, ifOlderThan: 10 }),
)

您也可以傳送查詢動作,但您需要負責實作任何其他邏輯。

手動預取的替代方法
dispatch(api.endpoints[endpointName].initiate(arg, { forceRefetch: true }))

預取範例

基本預取

這是一個非常基本的範例,顯示當使用者將滑鼠游標移至下一個箭頭時,您如何進行預取。這可能不是最佳的解決方案,因為如果他們將滑鼠游標移至、按一下,然後在不移動滑鼠的情況下變更頁面,我們將不知道要預取下一個頁面,因為我們不會看到下一個 onMouseEnter 事件。在這種情況下,您需要自行處理。您也可以考慮自動預取下一個頁面...

自動預取

在最後一個範例中,我們自動 prefetch 下一個頁面,看起來沒有網路延遲。

預取所有已知頁面

useQuery 初始化的第一個查詢執行後,我們會自動擷取所有剩餘頁面。