首頁 > web前端 > js教程 > React 中用於非同步資料擷取和快取的輕量級 Hook 展示

React 中用於非同步資料擷取和快取的輕量級 Hook 展示

Mary-Kate Olsen
發布: 2025-01-07 18:36:41
原創
479 人瀏覽過

Showcase of a Lightweight Hook for Async Data Fetching & Caching in React

大家好!

我一直在開發一個輕量級的React hook,我稱之為useAsync,它模仿React Query 的一些基本功能(如獲取、快取、重試等),但以更緊湊的方式,易於定制的包。以下是其內部工作原理的快速分解,引用了相關的程式碼部分。如果您想查看完整程式碼,請前往儲存庫:

GitHub 上的完整原始碼
此鉤子也可以在 npm 上作為 api-refetch.

使用

為什麼要自己做鉤子?

雖然 React Query 和 SWR 都是很棒的函式庫,但基於以下幾個原因,我想要更實用的方法:

  1. 輕量化

    雖然 React Query 和 SWR 功能豐富,但它們可能相對較大(React Query ~2.2 MB,SWR ~620 kB)。 api-refetch 大約 250 kB,使其非常適合較小的應用程式,其中包大小是一個大問題。該掛鉤意味著作為另一個庫(Intlayer)的依賴項安裝。因此,解決方案的大小是一個重要的考慮因素。

  2. 易於自訂和最佳化
    我需要一些特定的功能,例如從本地存儲存儲/獲取資料使用簡單的方法管理並行請求
    透過複製儲存庫將程式碼直接複製到您的專案中,您可以刪除任何不需要的功能並僅保留您需要的功能。這不僅可以減少捆綁包大小,還可以最大限度地減少不必要的重新渲染和增加,為您提供根據您的特定要求量身定制的更精簡、性能更高的解決方案。

  3. 無所需供應商

    我想避免使用 Context Provider 來使鉤子全局化,並使其使用盡可能簡單。所以我根據 Zustand 商店製作了 hooke 的一個版本(請參閱下面的範例)。

  4. 學習練習

    從頭開始建立非同步函式庫是理解並發、快取和狀態管理內部結構的絕佳方法。

簡而言之,滾動我自己的鉤子是一個機會精確磨練我需要的功能(並跳過我不需要的功能),同時保持庫小且易於理解。

涵蓋的功能

React 鉤子管理:

  • 取得與狀態管理:處理載入、錯誤、成功和取得狀態。
  • 快取和儲存:可選擇快取資料(透過底層的 React 狀態或 Zustand)並提供本地儲存支援。
  • 重試和重新驗證:可設定的重試限制和自動重新驗證間隔。
  • 啟動和失效:根據其他查詢或狀態自動啟動和失效查詢。範例:使用者登入時自動取得一些數據,並在使用者登出時使其失效。
  • 並行元件掛載取得:當多個元件同時掛載時,防止對相同資源同時發出多個要求。

程式碼如何運作

以下是 api-refetch 中的要點以及對 useAsync.tsx 中程式碼相關部分的簡短引用。

1. 並行安裝的取得與處理

  • 程式碼片段
  // This map stores any in-progress Promise to avoid sending parallel requests
  // for the same resource across multiple components.
  const pendingPromises = new Map();

  const fetch: T = async (...args) => {
    // Check if a request with the same key + args is already running
    if (pendingPromises.has(keyWithArgs)) {
      return pendingPromises.get(keyWithArgs);
    }

    // Otherwise, store a new Promise and execute
    const promise = (async () => {
      setQueryState(keyWithArgs, { isLoading: true });

      // ...perform fetch here...
    })();

    // Keep it in the map until it resolves or rejects
    pendingPromises.set(keyWithArgs, promise);
    return await promise;
  };
登入後複製
登入後複製
  • 說明:在這裡,我們將任何正在進行的提取儲存在pendingPromises 映射中。當兩個元件嘗試同時取得相同的資源(透過具有相同的 keyWithArgs)時,第二個元件只會重複使用正在進行的請求,而不是重複的網路呼叫。

2. 重新驗證

  • 程式碼片段
  // Handle periodic revalidation if caching is enabled
  useEffect(
    () => {
      if (!revalidationEnabled || revalidateTime <= 0) return; // Revalidation is disabled
      if (!isEnabled || !enabled) return; // Hook is disabled
      if (isLoading) return; // Fetch is already in progress
      if (!isSuccess || !fetchedDateTime) return; // Should retry either of revalidate
      if (!(cacheEnabled || storeEnabled)) return; // Useless to revalidate if caching is disabled

      const timeout = setTimeout(() => {
        fetch(...storedArgsRef.current);
      }, revalidateTime);

      return () => clearTimeout(timeout);
    },
    [
      /* dependencies */
    ]
  );
登入後複製
登入後複製
  • 說明:每當您啟用重新驗證時,api-refetch 都會檢查快取的資料是否早於指定的 revalidateTime。如果是,資料會在背景自動重新獲取,以使您的 UI 保持同步,無需額外的手動觸發。

3. 重試邏輯

  • 程式碼片段
  useEffect(
    () => {
      const isRetryEnabled = errorCount > 0 && retryLimit > 0;
      const isRetryLimitReached = errorCount > retryLimit;

      if (!isEnabled || !enabled) return; // Hook is disabled
      if (!isRetryEnabled) return; // Retry is disabled
      if (isRetryLimitReached) return; // Retry limit has been reached
      if (!(cacheEnabled || storeEnabled)) return; // Useless to retry if caching is disabled
      if (isLoading) return; // Fetch is already in progress
      if (isSuccess) return; // Hook has already fetched successfully

      const timeout = setTimeout(() => {
        fetch(...storedArgsRef.current);
      }, retryTime);

      return () => clearTimeout(timeout);
    },
    [
      /* dependencies */
    ]
  );
登入後複製
  • 解釋:發生錯誤時,掛鉤會計算已發生的失敗嘗試次數。如果它仍然低於 retryLimit,它會自動等待 retryTime 毫秒,然後再試一次。此過程將持續進行,直到成功獲取數據或達到重試限制。

4. 自動獲取

  • 程式碼片段
  // Auto-fetch data on hook mount if autoFetch is true
  useEffect(
    () => {
      if (!autoFetch) return; // Auto-fetch is disabled
      if (!isEnabled || !enabled) return; // Hook is disabled
      if (isFetched && !isInvalidated) return; // Hook have already fetched or invalidated
      if (isLoading) return; // Fetch is already in progress

      fetch(...storedArgsRef.current);
    },
    [
      /* dependencies */
    ]
  );
登入後複製
  • 說明:將 autoFetch 設為 true 時,鉤子會在元件安裝後立即自動執行非同步函數,非常適合您始終希望載入資料的「即發即忘」場景。

查看 GitHub 上的完整原始碼

查看完整程式碼,其中包含本機儲存邏輯、查詢失效等:

  • 完整原始碼

如果您有興趣,請隨意嘗試、回報問題或做出貢獻。非常感謝任何反饋!

使用範例

安裝

複製程式碼或編碼(repo)[https://github.com/aymericzip/api-refetch]


  // This map stores any in-progress Promise to avoid sending parallel requests
  // for the same resource across multiple components.
  const pendingPromises = new Map();

  const fetch: T = async (...args) => {
    // Check if a request with the same key + args is already running
    if (pendingPromises.has(keyWithArgs)) {
      return pendingPromises.get(keyWithArgs);
    }

    // Otherwise, store a new Promise and execute
    const promise = (async () => {
      setQueryState(keyWithArgs, { isLoading: true });

      // ...perform fetch here...
    })();

    // Keep it in the map until it resolves or rejects
    pendingPromises.set(keyWithArgs, promise);
    return await promise;
  };
登入後複製
登入後複製

快速範例

  // Handle periodic revalidation if caching is enabled
  useEffect(
    () => {
      if (!revalidationEnabled || revalidateTime <= 0) return; // Revalidation is disabled
      if (!isEnabled || !enabled) return; // Hook is disabled
      if (isLoading) return; // Fetch is already in progress
      if (!isSuccess || !fetchedDateTime) return; // Should retry either of revalidate
      if (!(cacheEnabled || storeEnabled)) return; // Useless to revalidate if caching is disabled

      const timeout = setTimeout(() => {
        fetch(...storedArgsRef.current);
      }, revalidateTime);

      return () => clearTimeout(timeout);
    },
    [
      /* dependencies */
    ]
  );
登入後複製
登入後複製

就是這樣!嘗試一下,然後讓我知道進展如何。 GitHub 上非常歡迎提供回饋、問題或貢獻。

GitHub: api-refetch

編碼愉快!

以上是React 中用於非同步資料擷取和快取的輕量級 Hook 展示的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板