首页  >  文章  >  web前端  >  如何构建处理顺序请求的 React Hook

如何构建处理顺序请求的 React Hook

WBOY
WBOY原创
2024-08-21 09:06:05693浏览

当你需要快速响应用户操作并从后端获取最新数据时,你可能需要一个支持顺序请求的 React Hook。如果之前的请求仍在进行,则此挂钩可以取消它们,并且仅返回最新的数据。这不仅提高了性能,还增强了用户体验。

构建一个简单的顺序请求 React Hook

让我们从构建一个简单的顺序请求 React hook 开始:

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 中取消 Promises”。你可以这样使用它:

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 Hook

如果我们需要更全面的顺序请求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 Hook的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn