」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 如何建構處理順序請求的 React Hook

如何建構處理順序請求的 React Hook

發佈於2024-11-04
瀏覽:233

當您需要快速回應使用者操作並從後端取得最新資料時,您可能需要一個支援順序請求的 React Hook。如果先前的請求仍在進行,則此掛鉤可以取消它們,並且僅返回最新的資料。這不僅提高了效能,還增強了使用者體驗。

建立一個簡單的順序請求 React Hook

讓我們從建立一個簡單的順序請求 React hook 開始:

import { useCallback, useRef } from 'react';
​
const buildCancelableFetch = (
  requestFn: (signal: AbortSignal) => Promise,
) => {
  const abortController = new AbortController();
​
  return {
    run: () =>
      new Promise((resolve, reject) => {
        if (abortController.signal.aborted) {
          reject(new Error('CanceledError'));
          return;
        }
​
        requestFn(abortController.signal).then(resolve, reject);
      }),
​
    cancel: () => {
      abortController.abort();
    },
  };
};
​
function useLatest(value: T) {
  const ref = useRef(value);
  ref.current = value;
  return ref;
}
​
export function useSequentialRequest(
  requestFn: (signal: AbortSignal) => Promise,
) {
  const requestFnRef = useLatest(requestFn);
  const currentRequest = useRef void } | null>(null);
​
  return useCallback(async () => {
    if (currentRequest.current) {
      currentRequest.current.cancel();
    }
​
    const { run, cancel } = buildCancelableFetch(requestFnRef.current);
    currentRequest.current = { cancel };
​
    return run().finally(() => {
      if (currentRequest.current?.cancel === cancel) {
        currentRequest.current = null;
      }
    });
  }, [requestFnRef]);
}

這裡的關鍵想法來自文章「How to Annul Promises in JavaScript」。你可以這樣使用它:

import { useSequentialRequest } from './useSequentialRequest';
​
export function App() {
  const run = useSequentialRequest((signal: AbortSignal) =>
    fetch('http://localhost:5000', { signal }).then((res) => res.text()),
  );
​
  return ;
}

這樣,當您多次快速點擊按鈕時,您只會獲取最新請求的數據,先前的請求將被丟棄。

How to Build a React Hook That Handles Sequential Requests

建構最佳化的順序請求 React Hook

如果我們需要更全面的順序請求React Hook,上面的程式碼還有改進的空間。例如:

  • 我們可以推遲創建 AbortController 直到實際需要時,減少不必要的創建成本。

  • 我們可以使用泛型來支援任何類型的請求參數。

這是更新版本:

import { useCallback, useRef } from 'react';
​
function useLatest(value: T) {
  const ref = useRef(value);
  ref.current = value;
  return ref;
}
​
export function useSequentialRequest(
  requestFn: (signal: AbortSignal, ...args: Args) => Promise,
) {
  const requestFnRef = useLatest(requestFn);
​
  const running = useRef(false);
  const abortController = useRef(null);
​
  return useCallback(
    async (...args: Args) => {
      if (running.current) {
        abortController.current?.abort();
        abortController.current = null;
      }
​
      running.current = true;
​
      const controller = abortController.current ?? new AbortController();
      abortController.current = controller;
​
      return requestFnRef.current(controller.signal, ...args).finally(() => {
        if (controller === abortController.current) {
          running.current = false;
        }
      });
    },
    [requestFnRef],
  );
}

請注意,在finally區塊中,我們檢查目前控制器是否等於abortController.current以防止競爭條件。這可確保只有活動請求才能修改運行狀態。

更全面的用法:

import { useState } from 'react';
import { useSequentialRequest } from './useSequentialRequest';
​
export default function Home() {
  const [data, setData] = useState('');
​
  const run = useSequentialRequest(async (signal: AbortSignal, query: string) =>
    fetch(`/api/hello?query=${query}`, { signal }).then((res) => res.text()),
  );
​
  const handleInput = async (queryStr: string) => {
    try {
      const res = await run(queryStr);
      setData(res);
    } catch {
      // ignore errors
    }
  };
​
  return (
    
       {
          handleInput(e.target.value);
        }}
      />
      
Response Data: {data}
> ); }

您可以在線上嘗試:當您快速輸入時,先前的請求將被取消,只顯示最新的回應。

How to Build a React Hook That Handles Sequential Requests


如果您覺得這有幫助, 請考慮訂閱 我的時事通訊,以獲取有關Web 開發的更多有用文章和工具。感謝您的閱讀!

版本聲明 本文轉載於:https://dev.to/zacharylee/how-to-build-a-react-hook-that-handles-sequential-requests-e49?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3