連続リクエストを処理する React フックを構築する方法

WBOY
リリース: 2024-08-21 09:06:05
オリジナル
728 人が閲覧しました

ユーザーのアクションに迅速に応答し、バックエンドから最新のデータをフェッチする必要がある場合、シーケンシャルなリクエストをサポートする React Hook が必要になる場合があります。このフックは、以前のリクエストがまだ進行中の場合はキャンセルし、最新のデータのみを返すことができます。これにより、パフォーマンスが向上するだけでなく、ユーザー エクスペリエンスも向上します。

単純なシーケンシャルリクエスト React フックの構築

簡単なシーケンシャルリクエスト React フックを構築することから始めましょう:

import { useCallback, useRef } from 'react';
​
const buildCancelableFetch = <T>(
  requestFn: (signal: AbortSignal) => Promise<T>,
) => {
  const abortController = new AbortController();
​
  return {
    run: () =>
      new Promise<T>((resolve, reject) => {
        if (abortController.signal.aborted) {
          reject(new Error('CanceledError'));
          return;
        }
​
        requestFn(abortController.signal).then(resolve, reject);
      }),
​
    cancel: () => {
      abortController.abort();
    },
  };
};
​
function useLatest<T>(value: T) {
  const ref = useRef(value);
  ref.current = value;
  return ref;
}
​
export function useSequentialRequest<T>(
  requestFn: (signal: AbortSignal) => Promise<T>,
) {
  const requestFnRef = useLatest(requestFn);
  const currentRequest = useRef<{ cancel: () => 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]);
}
ログイン後にコピー

ここでの重要なアイデアは、記事「JavaScript で Promise を破棄する方法」から来ています。次のように使用できます:

import { useSequentialRequest } from './useSequentialRequest';
​
export function App() {
  const run = useSequentialRequest((signal: AbortSignal) =>
    fetch('http://localhost:5000', { signal }).then((res) => res.text()),
  );
​
  return <button onClick={run}>Run</button>;
}
ログイン後にコピー

このようにすると、ボタンを素早く複数回クリックすると、最新のリクエストからのデータのみが取得され、以前のリクエストは破棄されます。

How to Build a React Hook That Handles Sequential Requests

最適化されたシーケンシャルリクエスト React フックの構築

より包括的なシーケンシャル リクエスト React Hook が必要な場合、上記のコードには改善の余地があります。例:

  • 実際に必要になるまで AbortController の作成を延期でき、不必要な作成コストを削減できます。

  • ジェネリックを使用して、あらゆるタイプのリクエスト引数をサポートできます。

更新バージョンは次のとおりです:

import { useCallback, useRef } from 'react';
​
function useLatest<T>(value: T) {
  const ref = useRef(value);
  ref.current = value;
  return ref;
}
​
export function useSequentialRequest<Args extends unknown[], Data>(
  requestFn: (signal: AbortSignal, ...args: Args) => Promise<Data>,
) {
  const requestFnRef = useLatest(requestFn);
​
  const running = useRef(false);
  const abortController = useRef<AbortController | null>(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 (
    <>
      <input
        placeholder="Please input"
        onChange={(e) => {
          handleInput(e.target.value);
        }}
      />
      <div>Response Data: {data}</div>
    </>
  );
}
ログイン後にコピー

オンラインで試すことができます。素早く入力すると、以前のリクエストはキャンセルされ、最新の応答のみが表示されます。

How to Build a React Hook That Handles Sequential Requests


これが役立つと思われた場合は、 Web 開発に関するさらに役立つ記事やツールを入手するには、ニュースレターの購読をご検討ください 。読んでいただきありがとうございます!

以上が連続リクエストを処理する React フックを構築する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート