使用 react-promise-tracker 无限获取请求

infinite fetch request with react-promise-tracker

我正在使用提取 API 从外部 API 提取数据。我想在请求获取数据时实现加载游标。为此,我正在尝试使用 react-promise-tracker 库。但是当使用库中的 trackPromise 时,提取会无限重复。

您可以在更改此示例项目中的注释代码时看到自己发生的错误(检查控制台这一切都发生在 console.log 中): https://codesandbox.io/s/tender-easley-szfxg?file=/src/App.tsx

基本上这是可行的:

export default function App() {
  const get = (url: string) => {
    let headers: {
      Accept: string;
      "Content-Type": string;
    } = {
      Accept: "application/ld+json",
      "Content-Type": "application/json"
    };
   
    return fetch(url, {
      method: "GET",
      headers
    } as RequestInit)
      .then((res: Response) => {
        if (res.ok) {
          return res.json();
        }

        return Promise.reject(res);
      })
      .catch(Promise.reject.bind(Promise));
  };
  get("https://608bb5b6737e470017b752e2.mockapi.io/users")
    .then(console.log)
    .catch(console.log);

  return <div>nothing</div>;
}

这不是:

import { usePromiseTracker, trackPromise } from "react-promise-tracker";   // ADDED CODE

export default function App() {
  const { promiseInProgress } = usePromiseTracker({ area: "fetchDataGet" });      // ADDED CODE
  const cursorStyle = () => {                                                     // ADDED CODE
    return promiseInProgress ? { cursor: "wait" } : undefined;                    // ADDED CODE
  };                                                                              // ADDED CODE
  const get = (url: string) => {
    let headers: {
      Accept: string;
      "Content-Type": string;
    } = {
      Accept: "application/ld+json",
      "Content-Type": "application/json"
    };
    return trackPromise(                                                           // ADDED CODE
      fetch(url, {
        method: "GET",
        headers
      } as RequestInit)
        .then((res: Response) => {
          if (res.ok) {
            return res.json();
          }

          return Promise.reject(res);
        })
        .catch(Promise.reject.bind(Promise)),
      "fetchDataGet"                                                               // ADDED CODE
    );
  };
  get("https://608bb5b6737e470017b752e2.mockapi.io/users")
    .then(console.log)
    .catch(console.log);

  return <div style={cursorStyle()}>nothing</div>;
}

第二个代码的输出是几个console.log,当它应该只有 1 并且在某些时候服务器 returns 429 错误(这是正常的,这只是服务器安全)

在您的第二个示例中,您没有使用 useEffect,而是在组件主体内部调用 get()(这意味着每次组件呈现时都会调用它)。 usePromiseTracker 将触发状态更改,因此每次您的请求承诺解决时都会重新呈现。这样肯定会导致抓取死循环

一般来说,永远不要无条件地触发组件主体内的任何副作用(如获取数据)。它应该始终位于仅在特定时间点运行的效果内或事件处理程序内。

在这种特定情况下,您可以通过扭曲 get() 调用效果来解决它:

useEffect(() => {
    get("https://608bb5b6737e470017b752e2.mockapi.io/users")
    .then(console.log)
    .catch(console.log);
}, []); // will only be called once after mounting

如果您的项目中经常有这种代码,您应该提取一个自定义挂钩,在内部执行此操作并隐藏 usePromiseTracker 这样您就不必在每次要获取时都显式使用它东西:

const useFetch = (area, url, options) => {
    const { promiseInProgress } = usePromiseTracker({area});

    useEffect(() => {
        trackPromise(                                                           
            fetch(url, options)
                .then(res => res.ok ? res.json() : Promise.reject(res))
                .catch(Promise.reject.bind(Promise)),
            area,                                                               
        );
    }, []);

    return promiseInProgress;
};